HTMHell
A collection of bad practices in HTML, copied from real websites.
2024-11-26T00:00:00Z
https://htmhell.dev/
Manuel Matuzovic
manuel@matuzo.at
HTML Input Validation is (maybe) Good
2025-12-28T00:00:00Z
https://htmhell.dev/adventcalendar/2025/28/
by Wes Goulet<br><p>I think of client-side validation as a progressive enhancement for your users. You have to validate user input on the server (you can't trust what comes from the client), but some validation on the client makes for a nice UX. But that doesn't have to mean lots of JS code or using some validation library on your client. You can get pretty far with the browser's built-in HTML input validation. And then you can layer a little bit of JS on top of that to make it even better.</p>
<p>Let's take a look at a text input. You can use <code>pattern</code>, <code>minlength</code> and <code>maxlength</code> to provide constraints. You can use <code>title</code> to provide error message. You can conditionally style invalid input with <code>:user-invalid</code>.</p>
<h2 id="example">Example</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>program_name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Name of program<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span><br /><span class="highlight-line"> <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><br /><span class="highlight-line"> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>program_name<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">minlength</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">maxlength</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">pattern</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[a-zA-Z0-9]+<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Only alphabetical and numerical characters are accepted<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">required</span></span><br /><span class="token punctuation">/></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">input:user-invalid</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 4px solid red<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>When the user attempts to submit the form the browser takes care of validating and showing the message.</p>
<figure style="margin: 4rem 0;">
<img src="https://htmhell.dev/adventcalendar/2025/28/chromium.jpg" alt="A screenshot of Chrome. The error message says 'Please match the requested format. Only alphabetical and numerical characters are accepted'" style="border: 1px solid #ccc;" />
<figcaption>Example validation message on Chrome</figcaption>
</figure>
<figure style="margin: 4rem 0;">
<img src="https://htmhell.dev/adventcalendar/2025/28/firefox.jpg" alt="A screenshot of Firefox. The error message says 'Please match the requested format: Only alphabetical and numerical characters are accepted.'" style="border: 1px solid #ccc;" />
<figcaption>Example validation message on Firefox</figcaption>
</figure>
<figure style="margin: 4rem 0;">
<img src="https://htmhell.dev/adventcalendar/2025/28/safari.jpg" alt="A screenshot of Safari. The error message says 'Match the requested format: Only alphabetical and numerical characters are accepted'" style="border: 1px solid #ccc;" />
<figcaption>Example validation message on Safari</figcaption>
</figure>
<figure style="margin: 4rem 0;">
<img src="https://htmhell.dev/adventcalendar/2025/28/ios.jpeg" alt="A screenshot of Safari on iOS. The error message says 'Match the requested format: Only alphabetical and numerical characters are accepted'" style="border: 1px solid #ccc;" />
<figcaption>Example validation message on Safari on iOS</figcaption>
</figure>
<blockquote>
<p>You can play with a live example at <a href="https://codepen.io/wes_goulet/pen/emJjqKj">this CodePen</a>.</p>
</blockquote>
<p>BTW, I just noticed that Chrome and Firefox say "Please" but Safari doesn't š</p>
<h2 id="styling">Styling</h2>
<p>You can't style the error popup, so if that's important to you then maybe you need to write your own error UI. A lot of times I don't mind leaning on browser UI when it's available (ie: most of the time I don't need my error messages in my website's overall brand/styling). Also, I think a lot of users have seen the browser's error UI before (from other sites that use native form validation), so there is some familiarity there for the user.</p>
<h2 id="the-problem-accessibility">The Problem: Accessibility</h2>
<p>Unfortunately, native form validation isn't very accessible.</p>
<p>I had assumed it was accessible, because most of the time when I lean on the browser to do something it handles accessibility much better than any userland code I would write. But after reading <a href="https://adrianroselli.com/2019/02/avoid-default-field-validation.html">this excellent post from Adrian Roselli</a> (an accessibility expert), I learned my assumption was wrong.</p>
<p>The main accessibility issues with native form validation are:</p>
<ul>
<li><strong>Error messages aren't associated with form fields</strong> - Screen readers don't reliably announce which field has an error, so users relying on assistive technology can't easily figure out what needs to be fixed.</li>
<li><strong>Error messages disappear too quickly</strong> - The browser's error bubble might disappear before users can read it.</li>
<li><strong>Error messages don't respect user text size/spacing preferences</strong> - The error bubble text doesn't resize with browser zoom settings or respect text spacing preferences.</li>
</ul>
<p>Related to all this, <a href="https://github.com/w3c/wcag/pull/4431">a recent update to WCAG</a> acknowledges these accessibility issues with native form validation.</p>
<h2 id="make-it-better-with-js">Make it better with JS</h2>
<p>I originally wrote this post to point out that native form validation is good enough (lean on the browser!) and that's all you need. But after reading about the accessibility issues, I think the right answer is using native form validation as the foundation, and then adding a bit of JS to make it more accessible.</p>
<p>We'll use the Constraint Validation API, which is the JavaScript interface for HTML form validation. It allows you to programmatically check if a form field is valid, get validation messages, and customize how errors are displayed to users. For example, you can check if an input is valid:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token keyword">const</span> element <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"program_name"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>element<span class="token punctuation">.</span><span class="token function">checkValidity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// returns true or false</span></span></code></pre>
<p>You can also get the validation message:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token keyword">const</span> element <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"program_name"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>element<span class="token punctuation">.</span>validationMessage<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// returns the error message if invalid</span></span></code></pre>
<p>We'll use the Constraint Validation API and follow <a href="https://www.w3.org/WAI/WCAG22/Understanding/error-identification.html">WCAG input error guidance</a> to create our own error messages that are properly associated with form fields. Here's a simple example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</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>my-form<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>program_name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Name of program<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span><br /><span class="highlight-line"> <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><br /><span class="highlight-line"> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>program_name<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">minlength</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">maxlength</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">pattern</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[a-zA-Z0-9]+<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Only alphabetical and numerical characters are accepted<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">required</span></span><br /><span class="highlight-line"> <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>program_name-error<span class="token punctuation">"</span></span></span><br /> <span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>program_name-error<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>alert<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token keyword">const</span> form <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"my-form"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">const</span> input <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"program_name"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">const</span> errorMessage <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"program_name-error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">// IMPORTANT: set this attribute in JS, that way it's a</span></span><br /><span class="highlight-line"><span class="token comment">// progressive enhancement (ie: if JS isn't available the</span></span><br /><span class="highlight-line"><span class="token comment">// native form validation will still work).</span></span><br /><span class="highlight-line">form<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"novalidate"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">validateInput</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> isValid <span class="token operator">=</span> input<span class="token punctuation">.</span><span class="token function">checkValidity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> errorMessage<span class="token punctuation">.</span>textContent <span class="token operator">=</span> isValid <span class="token operator">?</span> <span class="token string">""</span> <span class="token operator">:</span> input<span class="token punctuation">.</span>validationMessage<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>isValid<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> input<span class="token punctuation">.</span><span class="token function">removeAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-invalid"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> input<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-invalid"</span><span class="token punctuation">,</span> <span class="token string">"true"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">// Validate on blur (when user leaves the field)</span></span><br /><span class="highlight-line">input<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"blur"</span><span class="token punctuation">,</span> validateInput<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">// Clear errors as user types</span></span><br /><span class="highlight-line">input<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"input"</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></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token punctuation">.</span><span class="token function">checkValidity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> errorMessage<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> input<span class="token punctuation">.</span><span class="token function">removeAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-invalid"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">// Handle form submit</span></span><br /><span class="highlight-line">form<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"submit"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>form<span class="token punctuation">.</span><span class="token function">checkValidity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">// Update validation state for all fields</span></span><br /><span class="highlight-line"> <span class="token function">validateInput</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>In this example:</p>
<ul>
<li>The form has <code>novalidate</code> to turn off the browser's built-in validation (added via JavaScript so it degrades gracefully).</li>
<li>The error message is associated with the input using <code>aria-describedby</code>, so screen readers will announce it when the field is focused.</li>
<li>The error message has <code>role="alert"</code> so screen readers will announce it when it appears.</li>
<li>The input gets <code>aria-invalid</code> set appropriately, clearly marking it for assistive technologies.</li>
<li>Validation happens on <code>blur</code> (when the user leaves the field) and on form submit.</li>
<li>The error message stays visible, giving users time to read it.</li>
</ul>
<blockquote>
<p>You can play with a live example at <a href="https://codepen.io/wes_goulet/pen/emJjqKj">this CodePen</a>.</p>
</blockquote>
<p>This <a href="https://cloudfour.com/thinks/progressively-enhanced-form-validation-part-2-layering-in-javascript/">post by Cloud Four</a> spells out a more complete solution in detail. (If you mainly support evergreen browsers then I wouldn't worry too much about the first part "Removing invalid styles on page load for all browsers" since Chrome has shipped support for <code>:user-invalid</code> for <a href="https://caniuse.com/wf-user-pseudos">a couple years now</a>.)</p>
<p>So native HTML form validation is a good starting point, but it's not enough on its own due to accessibility issues. You can use the Constraint Validation API to layer on accessible error messages with a bit of JavaScript. That way you get the browser's validation working as a baseline, and then enhance it to be accessible when JS is available.</p>
<blockquote>
<p>Thanks to <a href="https://matuzo.at/">Manuel</a> for reviewing this post and pointing out the accessibility issues with native form validation.</p>
</blockquote>
Replacing JS with just HTML
2025-12-27T00:00:00Z
https://htmhell.dev/adventcalendar/2025/27/
by Aaron T. Grogg<br><p>For many years now, JavaScript has been the workhorse of the web. If you wanted to do something that couldn't be done with just HTML and CSS, you could usually find a way to do it with JS.<br />
And that is great! JS has helped push user experiences forward, and honestly helped push HTML and CSS forward!</p>
<p>But as time marches on, and the HTML and CSS methods <a href="https://webstatus.dev/?q=%28group%3Ahtml+OR+css%29&sort=name_asc">gain traction</a>, we need to start replacing the old JS methods that feel so comfy with new methods that require less JS.</p>
<p><em><strong>Nothing against JS</strong></em>, but it has better things to do than setup and manage your accordions or offscreen navigation menus... Plus, JS needs to be downloaded, decompressed, evaluated, processed, and then often consumes memory to monitor and maintain features. If we can <em>hand-off</em> any JS functionality to native HTML or CSS, then users can download less stuff, and the remaining JS can pay attention to more important tasks that HTML and CSS can't handle (yet).</p>
<p>Below are a few examples; any you care to add?</p>
<h2 id="table-of-contents">Table of Contents:</h2>
<ul>
<li><a href="https://htmhell.dev/adventcalendar/2025/27/#accordions-expanding-content-panels">Accordions / Expanding Content Panels</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2025/27/#input-with-autofilter-suggestions-dropdown">Input with Autofilter Suggestions Dropdown</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2025/27/#modals-popovers">Modals / Popovers</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2025/27/#offscreen-nav-content">Offscreen Nav / Content</a></li>
</ul>
<h2 id="accordions-/-expanding-content-panels">Accordions / Expanding Content Panels</h2>
<h3 id="description">Description:</h3>
<p>The <code>details</code> and <code>summary</code> HTML elements provide an HTML-only replacement to the typical JS accordion:<br />
<img src="https://htmhell.dev/adventcalendar/2025/27/accordion-expanding-content.gif" alt="Examples of HTML element expanding and contracting" /><br />
CopePen: <a href="https://codepen.io/aarontgrogg/pen/GgoOqVX">Accordion / Expanding Content</a></p>
<h3 id="use-cases">Use cases:</h3>
<ul>
<li>Hiding/showing content</li>
<li>Expanding content sections</li>
</ul>
<h3 id="basic-implementation">Basic implementation:</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>Initially closed, click to open<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Content is initially hidden, but can be revealed by clicking the summary.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<p>Add an <code>open</code> attribute to set the default appearance as "open":</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span> <span class="token attr-name">open</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>Initially open, click to close<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Content is initially visible, but can be hidden by clicking the summary.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<p>Use the same <code>name</code> attribute on all related <code>details</code> (like radio buttons) to restrict only one open panel at a time:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>foo<span class="token punctuation">"</span></span> <span class="token attr-name">open</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>Initially open, clicking others will close this<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Content is initially visible, but can be hidden by clicking the summary; only one panel can be open at a time.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>foo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>Initially closed, clicking will open this, and close others<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Content is initially hidden, but can be revealed by clicking the summary; only one panel can be open at a time.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>foo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>Initially closed, clicking will open this, and close others<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Content is initially hidden, but can be revealed by clicking the summary; only one panel can be open at a time.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<p>You can also customize the appearance with CSS and trigger the open/close via JS.</p>
<p>Learn more about the <code>details</code> element in the previously-published ā<a href="https://www.htmhell.dev/adventcalendar/2025/23">For the Love of <details></a>".</p>
<h3 id="resources">Resources:</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details">MDN <code>details</code> page</a></li>
<li><a href="https://www.codewithshripal.com/playground/html/details-element"><details> element</a></li>
</ul>
<h3 id="browser-compatibility">Browser compatibility:</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details#browser_compatibility">MDN <code>details</code> Browser Compatibility</a></li>
</ul>
<h2 id="input-with-autofilter-suggestions-dropdown">Input with Autofilter Suggestions Dropdown</h2>
<h3 id="description-2">Description:</h3>
<p>Combining the HTML <code>input</code> and <code>datalist</code> elements can create a dropdown of options that autofilters as you type:<br />
<img src="https://htmhell.dev/adventcalendar/2025/27/input-with-datalist.gif" alt="Examples of HTML and providing autofilter dropdown" /><br />
CodePen: <a href="https://codepen.io/aarontgrogg/pen/yyePPor">Input with Autofilter Suggestions Dropdown</a></p>
<h3 id="use-cases-2">Use cases:</h3>
<ul>
<li>Site search</li>
<li>Product search or filter</li>
<li>Filter any list of data</li>
</ul>
<h3 id="basic-implementation-2">Basic implementation:</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>browser<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Browser<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</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><br /><span class="highlight-line"> <span class="token attr-name">list</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>browsers<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>browser<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>browser<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">size</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>off<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>datalist</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>browsers<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Arc<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Brave<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Chrome<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>DuckDuckGo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Firefox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microsoft Edge<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Opera<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Safari<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Tor<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Vivaldi<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>datalist</span><span class="token punctuation">></span></span></span></code></pre>
<p>You can also use other input types:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>quantity<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Quantity<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>number<span class="token punctuation">"</span></span> <br /><span class="highlight-line"> <span class="token attr-name">list</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>quantity-options<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>quantity<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>quantity<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>datalist</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>quantity-options<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</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><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>datalist</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>appointment<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Appointment<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>time<span class="token punctuation">"</span></span> <br /><span class="highlight-line"> <span class="token attr-name">list</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>appointments<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>appointment<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>appointment<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>datalist</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>appointments<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>12:00<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>13:00<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>14:00<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>datalist</span><span class="token punctuation">></span></span></span></code></pre>
<p>Note that, at the time of this writing, Firefox was limited to only textual-based input types, so no <code>date</code>, <code>time</code>, <code>range</code> or <code>color</code> for now... :-(</p>
<p>Also note that, at the time of this writing, <a href="https://htmhell.dev/adventcalendar/2025/5/#the-list-attribute-on-lessinputgreater-elements">there are limitations on mobile, and accessibility concerns</a>.</p>
<h3 id="resources-2">Resources:</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist">MDN <code>datalist</code> page</a></li>
<li><a href="https://www.sitepoint.com/html5-datalist-autocomplete/">HTML5 datalist autocomplete</a></li>
</ul>
<h3 id="browser-compatibility-2">Browser compatibility:</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist#browser_compatibility">MDN <code>datalist</code> Browser Compatibility</a></li>
</ul>
<h2 id="modals-/-popovers">Modals / Popovers</h2>
<h3 id="description-3">Description:</h3>
<p>The <code>popover</code> and <code>popovertarget</code> attributes can replace the traditional JS-driven modal/popover/overlay:<br />
<img src="https://htmhell.dev/adventcalendar/2025/27/modal-popover.gif" alt="Examples of HTML elements opening and closing" /><br />
CodePen: <a href="https://codepen.io/aarontgrogg/pen/QwyOKNW">Modal / Popover</a></p>
<h3 id="use-cases-3">Use cases:</h3>
<ul>
<li>Hiding/showing side panels / additional information</li>
</ul>
<h3 id="basic-implementation-3">Basic implementation</h3>
<p>An <code>auto</code> popover (default) can be "light dismissed" (clicking outside of it or hitting the <code>esc</code> key). Opening an <code>auto</code> automatically closes any other <code>auto</code> popovers that were open. Clicking the <code>button</code> a second time will close the one it opened.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">popovertarget</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pop-auto<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Toggle Popover</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</span> <span class="token attr-name">popover</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>pop-auto<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> I'm an "auto" Popover!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dialog</span><span class="token punctuation">></span></span></span></code></pre>
<p>A <code>hint</code> popover can also be "light dismissed". It does <em>not</em> close other <code>hint</code> popovers when opened. Clicking the <code>button</code> a second time will close the one it opened.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">popovertarget</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pop-hint<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Toggle Popover</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</span> <span class="token attr-name">popover</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>hint<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>pop-hint<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> I'm a "hint" Popover!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dialog</span><span class="token punctuation">></span></span></span></code></pre>
<p>Note that, at the time of this writing, Firefox and all iOS varieties do not support <code>hint</code> popovers.</p>
<p>A <code>manual</code> popover can <em>not</em> be "light dismissed". It does <em>not</em> close other <code>manual</code> popovers when opened. Clicking the <code>button</code> a second time will close the one it opened.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">popovertarget</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pop-manual<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Toggle Popover</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</span> <span class="token attr-name">popover</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>manual<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>pop-manual<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> I'm a "manual" Popover!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dialog</span><span class="token punctuation">></span></span></span></code></pre>
<p>Learn more about the opening and closing dialogs and popovers in the previously-published ā<a href="https://www.htmhell.dev/adventcalendar/2025/7">Controlling dialogs and popovers with the Invoker Commands API</a>".</p>
<h3 id="resources-3">Resources:</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/popover">MDN <code>popover</code> page</a></li>
<li><a href="https://mdn.github.io/dom-examples/popover-api/">Popover API examples</a></li>
<li><a href="https://developer.chrome.com/blog/introducing-popover-api/">Introducing the popover API</a></li>
<li><a href="https://codepen.io/jh3y/pen/XWPBmmo">Popover API!</a></li>
<li><a href="https://www.youtube.com/watch?v=KX8YQW7stzs">HTMHell #5 - An introduction to the popover attribute</a></li>
<li><a href="https://www.youtube.com/watch?v=RVxl5nZY790">The accessibility of the popover attribute</a></li>
</ul>
<h3 id="browser-compatibility-3">Browser compatibility:</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/popover#browser_compatibility">MDN <code>popover</code> Browser Compatibility</a></li>
</ul>
<h2 id="offscreen-nav-/-content">Offscreen Nav / Content</h2>
<h3 id="description-4">Description:</h3>
<p>The above Modal / Popover functionality can also be used to create an offscreen navigation that requires no JS:<br />
<img src="https://htmhell.dev/adventcalendar/2025/27/offscreen-nav.gif" alt="Example of HTML Popover used as an offscreen navigation menu" /></p>
<p>CodePen: <a href="https://codepen.io/aarontgrogg/pen/wBMPMVG">Offscreen Content</a></p>
<h3 id="use-cases-4">Use cases:</h3>
<ul>
<li>Hiding/showing navigation menus</li>
</ul>
<h3 id="basic-implementation-4">Basic implementation:</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">popovertarget</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>menu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Toggle Menu</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span> <span class="token attr-name">popover</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>menu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Nav Content</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">#menu</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 100vh<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">translate</span><span class="token punctuation">:</span> -100vw<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">#menu:popover-open</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">translate</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>I use a <code>nav</code> element to give it semantic value, but you can use any HTML element (<code>div</code>, <code>section</code>, <code>aside</code>, etc.).</p>
<p>A <code>popover</code> defaults to <code>position: fixed</code> per the User Agent Stylesheet, and is simply pushed off screen when closed, and pulled back onscreen when it is open. Note that <code>margin: 0</code> is required if you want to override the User Agent center-alignment.</p>
<p>Clicking outside of the above menu closes it. You can force the panel to stay open, requiring a manual/explicit close, by using <code>popover="manual"</code>.</p>
<p>You can also add a <code>backdrop</code> pseudo element and style it as you wish:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">#menu::backdrop</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>190 190 190 / 75%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="resources-4">Resources:</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/popover">MDN <code>popover</code> page</a></li>
<li><a href="https://mdn.github.io/dom-examples/popover-api/">MDN Popover API examples</a></li>
<li><a href="https://developer.chrome.com/blog/introducing-popover-api/">Introducing the popover API</a></li>
<li><a href="https://codepen.io/jh3y/pen/XWPBmmo">Popover API!</a></li>
</ul>
<h3 id="browser-compatibility-4">Browser compatibility:</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/popover#browser_compatibility">MDN <code>popover</code> Browser Compatibility</a></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>While we all love the power and flexibility JS provides, we should also respect it, and our users, by limiting its use to what it <em>needs</em> to do.</p>
<p>There is <strong>so</strong> much more that has changed in recent years, including a ton of options that CSS now covers. If you are now hungry for more, have a look at [my longer article that covers those as well](<a href="https://aarontgrogg.com/blog/2023/05/31/replace-js-with-no-js-or-lo-js-options/">https://aarontgrogg.com/blog/2023/05/31/replace-js-with-no-js-or-lo-js-options/</a>.</p>
<p>Happy reducing!<br />
Atg</p>
Class names for content not design
2025-12-26T00:00:00Z
https://htmhell.dev/adventcalendar/2025/26/
by Darice de Cuba<br><p>One of the first thing I learned when I started coding HTML, besides using <code>div</code> instead of <code>tables</code> for the layout, was naming classes. One of the core ideas being that with HTML and CSS we keep content separate from design. A website should have solid HTML and class names in such a way that you can redesign the website by only editing the CSS and maybe a little bit of the HTML.</p>
<p>But to do that, the class and id names need to be reusable. If you make the class names design and layout specific, you will have to edit your HTML files every time you want to change these elements their position or design, instead of only editing the CSS file.</p>
<p>Due to the use of frameworks, a lot of sites nowadays have become a div and class soup that makes the HTML code unreadable and hard to troubleshoot. Developers who never learnt to code HTML from scratch lack the basic understanding of a solid HTML layout with good class names.</p>
<h2 id="generic-class-names">Generic class names</h2>
<p>Some example of bad class names are:</p>
<ul>
<li>
<p><code><h1 class="text-header-blue"></code> a better way is <code><h1 class="site-name"></code> and for articles <code><h1 class="post-title"></code></p>
</li>
<li>
<p><code><nav class="top"></code> a better way is <code><nav class="main"></code></p>
</li>
<li>
<p><code><li class="border-bottom"></code> a better way is <code><li class="selected"></code></p>
</li>
</ul>
<p>Class names shouldnāt be design dependent, but specific to content. A text header wonāt always be blue, but it will always be the site name. It also makes it easier to read your HTML code 3 months later and not be confussed about what is what.</p>
<p>Best practice is to never use, colours, spacing, marking, etc. as class names. Only generic names that are independent of how the layout and design looks like.</p>
<p>A basic weblog homepage can look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>meta<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</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>date<span class="token punctuation">"</span></span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2025-12-26T19:00<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> December 26, 2025 <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>category<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HTML<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</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>post-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://www.htmhell.dev<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>How to HTML<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>intro<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut</span><br /><span class="highlight-line"> labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco</span><br /><span class="highlight-line"> laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in</span><br /><span class="highlight-line"> voluptate velit esse cillum dolore eu fugiat nulla pariatur.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https:/www.htmhell.dev/adventcalendar/2025/26/<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>read-more<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Read more </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> about āHow to HTMLā</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<p>The above HTML example is completely design and layout neutral. The layout and design can be edited only with CSS without having to change things in the HTML.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Keep your class and variable names generic. You want to avoid having to go through all your HTML and CSS code to change names to keep the code readable. There is nothing more painful than having to come back after each design or layout edit and be at lost what is what because <code><h1 class="serif-text-header-blue"></code> is not blue any more, but a sans font in bright pink.</p>
Abbreviations done right: The <abbr> element and why not use it
2025-12-25T00:00:00Z
https://htmhell.dev/adventcalendar/2025/25/
by Alexander Muzenhardt<br><h2 id="introduction">Introduction</h2>
<p>Abbreviations are great. They save time and space, make things efficient and tidy, and can even improve readability. But with the wrong technique, these abbreviations can turn into hell when it comes to accessibility.</p>
<h2 id="the-perfect-element-for-abbreviations">The perfect element for abbreviations</h2>
<p>So, the first thing you do: you look up āabbreviations in HTMLā and land straight on MDN (Mozilla Developer Network), at the <code><abbr></code> element. <code><abbr></code> is the short form for abbreviation. The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/abbr">documentation</a> shows exactly how to use it properly ā and even has an accessibility section. It says that <code><abbr></code> helps people understand what an abbreviation means, especially when it is technical or industry jargon.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>The <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>abbr</span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Mozilla Developer Network<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>MDN<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>abbr</span><span class="token punctuation">></span></span> is a documentation repository and learning resource for web developers.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>Perfect. Exactly what you were looking for. Semantic, accessible ā what could go wrong?<br />
So you start using <code><abbr></code> everywhere. However, after a while you get feedback from people asking what those abbreviations mean <em>and</em> an accessibility report complaining about ⦠those very same abbreviations.</p>
<p>Why? According to MDN, you did everything right.</p>
<h2 id="the-problem-with-abbr">The problem with abbr</h2>
<p>The biggest problem with the <code><abbr></code> element is that its <code>title</code> attribute behaves completely inconsistently across browsers, and some assistive technologies do not read parts of it at all.<br />
Adrian Roselli has already run extensive tests on his blog article ā<a href="https://adrianroselli.com/2024/01/using-abbr-element-with-title-attribute.html#Testing">Using abbr Element with title Attribute</a>ā showing that the <code>title</code> attribute on <code><abbr></code> elements is not announced by assistive technologies.</p>
<p>On top of that, the <code>title</code> attribute is a problem on touch devices ā there is simply no way to access it on a smartphone. It only appears on hover, and hover does not exist on touchscreens.</p>
<p>No matter how you look at it, when you use <code><abbr></code>, at least one group of users will always miss out on information they need.</p>
<h2 id="the-simple-solution-we-are-all-looking-for">The simple solution we are all looking for</h2>
<p>The easiest way to make abbreviations accessible is to spell them out the first time they appear, followed by the abbreviation in parentheses or vice versa.<br />
After that, you can simply use the abbreviation throughout the rest of the text ā itās already been explained once.</p>
<p>This approach is also accepted by WCAG (Web Content Accessibility Guidelines) 2.2 as a āSufficient Techniqueā under ā<a href="https://www.w3.org/WAI/WCAG22/Techniques/general/G97">G97: Providing the first use of an abbreviation immediately before or after the expanded form</a>ā.<br />
And the best part: you do not even need to touch your HTML markup ā you just make your text a bit clearer.</p>
<h3 id="examples">Examples</h3>
<h4>First example</h4>
<p>In this example, the abbreviation comes first, followed by its full meaning in parentheses.<br />
"The WAI (Web Accessibility Initiative) demonstrates the W3C (World Wide Web Consortium) commitment to accessibility."</p>
<h4>Second example</h4>
<p>The reverse is equally correct and accessible.<br />
"The United Nations High Commissioner for Human Rights (UNHCR) was established in 1950 to provide protection and assistance to refugees."</p>
<p>The key is consistency: do not switch between the two styles on the same website. Pick one and stick with it.</p>
<h2 id="the-alternatives-you-may-need">The alternatives you may need</h2>
<p>Below are three possible alternatives. They vary in complexity, and when implementing them, accessibility should always remain a priority. This is especially true for the third option ā a dictionary search ā where the input field and surrounding UI also need to be accessible.</p>
<p>I am intentionally leaving out implementation details here, as they would go beyond the scope of this article. These examples rely on other WCAG techniques that are not directly related to abbreviations. You will find plenty of examples online ā and if you have questions about any of these approaches, feel free to reach out.</p>
<h3 id="linking-to-definitions">Linking to definitions</h3>
<p>Of course, spelling out abbreviations is not the only option. You can also link an abbreviation to a page that explains it in detail. There are several ways to do this, all clearly outlined in ā<a href="https://www.w3.org/WAI/WCAG22/Techniques/general/G55">G55: Linking to definitions</a>ā.</p>
<h3 id="providing-a-glossary">Providing a glossary</h3>
<p>Another option is to provide an internal or external glossary that lists and explains all abbreviations used on your website. You can either link to it or simply reference it when abbreviations appear.<br />
Just like in books, glossaries can live at the end of a site or section ā and readers can look up terms as needed.<br />
A clear explanation of this approach can be found under ā<a href="https://www.w3.org/WAI/WCAG22/Techniques/general/G62">G62: Providing a glossary</a>ā.</p>
<h3 id="providing-a-function-to-search-an-online-dictionary">Providing a function to search an online dictionary</h3>
<p>A more elegant approach is to include a search feature on your site that queries an online dictionary for abbreviations and displays relevant results.<br />
You can find more information on this technique in ā<a href="https://www.w3.org/WAI/WCAG22/Techniques/general/G70">G70: Providing a function to search an online dictionary</a>ā.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Skip the <code><abbr></code> element altogether.<br />
Right now, it does not add any real value ā it only creates a false sense that your abbreviations are truly accessible and understandable by all persons.</p>
<p>The best and by far simplest solution is to clearly spell out each abbreviation the first time you use it, and then stick to the short form for the rest of the text.</p>
<p>Cheers<br />
Alex</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://adrianroselli.com/2024/01/using-abbr-element-with-title-attribute.html">Using abbr Element with title Attribute</a> - by Adrian Roselli</li>
<li><a href="https://heydonworks.com/article/the-abbr-element/#main">THE ABBR ELEMENT</a> - by Heydon Pickering</li>
<li><a href="https://stolperfrei.digital/html-abbr-tag/">The HTML abbr-tag ā Managing Abbreviations the Right Way (German)</a> - by Ria Weyprecht</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/abbr"><abbr>: The Abbreviation element</a> - by mdn</li>
<li><a href="https://www.w3.org/WAI/WCAG22/Understanding/abbreviations.html">Understanding SC 3.1.4: Abbreviations (Level AAA)</a> - by W3C</li>
<li><a href="https://www.w3.org/WAI/WCAG22/Techniques/general/G55">Technique G55: Linking to definitions</a> - by W3C</li>
<li><a href="https://www.w3.org/WAI/WCAG22/Techniques/general/G62">Technique G62: Providing a glossary</a> - by W3C</li>
<li><a href="https://www.w3.org/WAI/WCAG22/Techniques/general/G70">Technique G70: Providing a function to search an online dictionary</a> - by W3C</li>
<li><a href="https://www.w3.org/WAI/WCAG22/Techniques/general/G97">Technique G97: Providing the first use of an abbreviation immediately before or after the expanded form</a> - by W3C</li>
</ul>
The three semantics of HTML
2025-12-24T00:00:00Z
https://htmhell.dev/adventcalendar/2025/24/
by Tomasz Jakut<br><p>There is always that one elephant in the room alongside HTML ā <em>semantics</em>. You can ignore it for quite a while, but at the end of the day, you'll need to acknowledge that it's there, cluttering half of the space.</p>
<h2 id="our-beloved-elephant">Our beloved elephant</h2>
<p>So let's examine this exotic beast! According to Longman Dictionary of Contemporary English, <a href="https://www.ldoceonline.com/dictionary/semantics">semantics is <q>the meaning of a word or expression</q></a>. In our case, a word is an HTML element. And how can we check what the meaning of a word is? Well, we've already done that ā we've checked the semantics of the semantics (inception!). Fortunately, there is also a dictionary for HTML ā it's called <a href="https://html.spec.whatwg.org/multipage/"><cite>HTML Living Standard</cite></a>. It even <a href="https://html.spec.whatwg.org/multipage/dom.html#semantics-2">says it contains meanings</a>:</p>
<blockquote>
<p>Elements, attributes, and attribute values in HTML are defined (by this specification) to have certain meanings (semantics).</p>
</blockquote>
<p>Detailed descriptions of all HTML elements are in <a href="https://html.spec.whatwg.org/multipage/#toc-semantics">chapter 4, called <cite>The elements of HTML</cite></a>.</p>
<p class="highlight"><strong>Note:</strong> That's why I'm not keen on the <q>semantic elements</q> concept. For me, they are all semantic ā even <code><div></code> ā since their meanings are described in the specification.</p>
<p>But what if I told you there are actually three elephants in the room?</p>
<h2 id="first-semantics-for-users">First semantics: for users</h2>
<p>This is the most straightforward one of them all. That's the most basic role of semantics: to provide the end user with all the information necessary to correctly understand the content.</p>
<p>This type of semantics can be compared to formatting a text document in Word. You can always select some text and make it bigger and bolder, pretending it's a heading. But then you won't be able to generate a Table of Contents for your document with 100+ pages. The same is true for HTML ā except it's the user that will suffer. Let's look at an example ā my short bio:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>Comandeer<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>comandeer.avif<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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>Proud HTML Expert<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>It's still "semantic HTML" (because every HTML element has a meaning!), yet it's not especially useful. And yes, we can style it in any imaginable way (I bet we can even make it do the <a href="https://en.wikipedia.org/wiki/Can-can">can-can</a> with a bit of CSS magic) ā but it's still just a bunch of <code><div></code>s <a href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-div-element">without any intrinsic meaning</a>:</p>
<blockquote>
<p>The <code>div</code> element has no special meaning at all.</p>
</blockquote>
<p>Just like this big, bold text in Word ā if it's not a heading, it's not a heading.</p>
<p>Let's focus on raw HTML, forgetting about CSS. If we load the above code in the browser, it will look like this:</p>
<img src="https://htmhell.dev/adventcalendar/2025/24/html-div.png" alt=""Comandeer&qout; is followed by a logo and &qout;Proud HTML Expert" ā all text is unstyled." />
<p>It's plain text. And in terms of HTML semantics, that's the truth ā we used all these HTML elements to mark up plain text. Good job!</p>
<p>Fortunately, it can be easily fixed:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Comandeer<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>comandeer.avif<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>A photo of a smiling Comandeer.<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Proud HTML Expert<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<ul>
<li>The wrapper <code><div></code> element was replaced with the <a href="https://html.spec.whatwg.org/multipage/sections.html#the-article-element"><code><article></code> one</a> ā a bio can be treated as a <q>complete, or self-contained, composition in a document</q>.</li>
<li>The <code><div></code> element with my name was replaced with the <a href="https://html.spec.whatwg.org/multipage/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements"><code><h1></code> element</a> to indicate the purpose of the <code><article></code>.</li>
<li>The image got a <a href="https://html.spec.whatwg.org/multipage/images.html#alt">descriptive <code>alt</code> attribute</a>, describing its content.</li>
<li>The last <code><div></code> was replaced with the <a href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-p-element"><code><p></code> element</a> ā after all, it's a paragraph of text.</li>
</ul>
<p>If we open the document in the browser, it looks <em>better</em>:</p>
<img src="https://htmhell.dev/adventcalendar/2025/24/html-article.png" alt="The "Comandeer" is now bigger and bolder, indicating that it is a heading and the "Proud HTML Expert" got a margin around itself." />
<p>In other words, we <em>formatted</em> our text!</p>
<p class="highlight"><strong>Note:</strong> Each HTML element has its own <a href="https://html.spec.whatwg.org/multipage/rendering.html#rendering">default styles</a>, used to display it when there are no stylesheets provided by the website owner. This is the purest achievable form of HTML in a browser.</p>
<p>But it isn't particularly useful on its own. After all, there's always some CSS to make things prettier ā why should we care about choosing appropriate HTML elements? Because the Web should not be only visual. One of the main principles of Web Content Accessibility Guidelines (WCAG) is <a href="https://w3c.github.io/wcag/guidelines/22/#perceivable"><q>Perceivable</q></a>:</p>
<blockquote>
<p>Information and user interface components must be presentable to users in ways they can perceive.</p>
</blockquote>
<p>In other words, if someone can't perceive visual information, it <strong>must</strong> be available for them in another form. A bunch of styled <code><div></code>s do not convey any meaning without their visuals. And that's problematic for assistive technology, like <a href="https://developer.mozilla.org/en-US/docs/Glossary/Screen_reader">screen readers</a>. Screen readers use an <a href="https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree">accessibility tree</a> ā a DOM-like structure, built from the page's HTML and containing information about roles and purposes of all HTML elements. Let's see what that tree looks like for the <code><div></code> version of the page:</p>
<img src="https://htmhell.dev/adventcalendar/2025/24/a11y-div.png" alt="Both "Comandeer" and "Proud HTML Expert" are marked up as static text." />
<p>It's just a text. More importantly, the image is missing, due to its empty <code>alt</code> attribute. The <code><article></code> version is much better:</p>
<img src="https://htmhell.dev/adventcalendar/2025/24/a11y-article.png" alt="The whole bio is wrapped in the article, the "Comandeer" is correctly identified as a heading, the image has a description of "A photo of smiling Comandeer" and the "Proud HTML Expert" is marked up as a paragraph." />
<p>Thanks to that, the screen reader can successfully transform the page content into voice or braille and inform the user where the articles, headings, and other elements are. It would not only allow the user to fully understand the page (similarly to how the non-screen-reader user perceived the visual version of the page) but also allow the screen reader to provide better ways of navigating the page, e.g., by <a href="https://webaim.org/projects/screenreadersurvey10/#finding">jumping to headings</a>.</p>
<p>In other words, caring about semantics is caring for the user!</p>
<h2 id="second-semantics-for-user-agents">Second semantics: for user agents</h2>
<p>Semantics can also be used to provide additional information for user agents. And I deliberately use this term in quite a wide meaning, to mean any machine or software acting on behalf of its user. The most obvious example of a user agent is a browser (which is the term's typical meaning), but I would extend that name also to search engines. With a long history of <a href="https://en.wikipedia.org/wiki/Search_engine_optimization">SEO (Search Engine Optimization)</a>, it's not a mystery that search engines can and <em>do</em> understand HTML ā just to mention everlasting discussions about the importance of heading elements. Also, browsers can use semantics to provide affordances for users, like <a href="https://medium.com/@mandy.michael/building-websites-for-safari-reader-mode-and-other-reading-apps-1562913c86c9">using the <code><article></code> element to extract content in a reader mode</a>. But there are also other ways to provide yet another layer of semantics just for machines.</p>
<p>The most ambitious take on semantics for user agents is Web 3.0. And no, I don't mean Web3, based on blockchain. Long before that, back in the 2000s, W3C imagined a new type of web, as they called it ā <a href="https://www.w3.org/2007/Talks/0123-sb-W3CEmergingTech/Overviewp.pdf">Web of Data and Services, aka Semantic Web</a>. The idea itself was quite simple yet ingenious: the Web is full of data, but there was no way to enable effective communication between different websites and apps. Back then, machines were also far worse at discovering and understanding data. So they needed us, humans, to <em>mark up</em> data for them. The most basic way of marking stuff up is using HTML. But several additional technologies were developed to allow embedding additional metadata into websites. One of the first was <a href="https://www.w3.org/RDF/">Resource Description Framework (RDF)</a>. It's a <em>huge</em> standard, divided into several specifications. Yet it has one, pretty serious, downside: it's not built on top of HTML, as it has its own syntaxes (yes, plural). That's why <a href="https://rdfa.info/">RDFa</a> has been developed. It's a "lighter" version of RDF and can be used inside HTML via attributes:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token attr-name">vocab</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://schema.org/<span class="token punctuation">"</span></span> <span class="token attr-name">typeof</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Person<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">property</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>Comandeer<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>comandeer.avif<span class="token punctuation">"</span></span> <span class="token attr-name">property</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image<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>A photo of a smiling Comandeer.<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Proud <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">property</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>jobTitle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HTML Expert<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code>vocab</code> attribute points to the vocabulary we want to use and the <code>typeof</code> one ā a type of data we try to mark up. In our example, we want to spice up my bio with some additional metadata. Due to that, we decided to use the <code>Person</code> type (as I'm personally a person). Each property of, well, <em>me</em> is marked up with the <code>property</code> attribute, e.g., the heading with the name now has the <code>property="name"</code> attribute.</p>
<p>But of course, <a href="https://xkcd.com/927/">there is always more than one way to do something</a>. HTML has its own way of marking up additional metadata, so-called <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Microdata">microdata</a>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token attr-name">itemscope</span> <span class="token attr-name">itemtype</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://schema.org/Person<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">itemprop</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>Comandeer<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>comandeer.avif<span class="token punctuation">"</span></span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image<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>A photo of a smiling Comandeer.<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Proud <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>jobTitle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HTML Expert<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<p>It's pretty similar to RDFa. Instead of <code>vocab</code> + <code>typeof</code> attributes, it uses <code>itemscope</code> + <code>itemtype</code> ones, and the <code>property</code> one is replaced with <code>itemprop</code> one.</p>
<p>And if we want something more like RDF (so a separate thing from HTML), there is always <a href="https://json-ld.org/">JSON-LD</a> ā a way to express metadata in JSON. I also need to make one honorable mention ā <a href="https://microformats.io/">microformats</a>. They are the OGs of metadata in HTML. They use the <code>class</code> attribute to provide additional info about the content:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</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>h-card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</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>p-name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Comandeer<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>comandeer.avif<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>u-photo<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>A photo of a smiling Comandeer.<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Proud <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>p-job-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HTML Expert<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <a href="https://microformats.org/wiki/h-card">hCard microformat</a> is used for marking up information about people. What I love about microformats is the fact that they are Just HTML⢠without any fancy attributes.</p>
<p class="highlight"><strong>Note:</strong> Personally, I'm a fan of the RDFa format, so I'll stick to it for the rest of the article.</p>
<p>But what are vocabularies? In short, they are catalogs of things that can be described, like people, events, books, and places. Each of these things has its own unique set of properties (people have names, places have addresses, events have datesā¦). Things can also be mixed together (events are organized by people, etc.). Thanks to that, even more complex pieces of reality can be described in a manner understandable by machines.</p>
<p>In our example, the <a href="https://schema.org/">Schema.org</a> vocabulary is used. <a href="http://schema.org/">Schema.org</a> is a community effort, founded by Google, Microsoft, Yahoo, and Yandex, with a goal to create an open standard for structured data on the Web. This data is then used by search engines to provide additional information about the websites' content. <a href="https://developers.google.com/search/docs/appearance/structured-data/search-gallery">Google provides a gallery</a> of how it uses the structured data to prepare more detailed search results.</p>
<p>The only problem with Web 3.0 is that it never happened. Granted, we have several great technologies to embed additional metadata. But at the end of the day, they're used mainly by search engines. The ambitious vision of the Web where machines talk to each other and exchange data to provide better services to their users is still just a vision.</p>
<h2 id="third-semantics-for-web-developers">Third semantics: for web developers</h2>
<p>And finally, we can leave the vast land of user-centered semantics and enter the cozy territory of developer-centered semantics. Because an easily understandable HTML is also an easily maintainable HTML. And if we use HTML elements as intended (like creating lists with <code><ul></code> and <code><li></code> elements instead of <code><div></code>s), we're already halfway there!</p>
<p>But sometimes additional info is needed, e.g., a page contains several <code><article></code> elements and we want to easily distinguish between them. In that case, we can go back to webdev roots and use the <code>class</code> attribute.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token attr-name">vocab</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://schema.org/<span class="token punctuation">"</span></span> <span class="token attr-name">typeof</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Person<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>bio<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">property</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">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bio__name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Comandeer<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>comandeer.avif<span class="token punctuation">"</span></span> <span class="token attr-name">property</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image<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>A photo of a smiling Comandeer.<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>bio__image<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>bio__description<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Proud <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">property</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>jobTitle<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>bio__job-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HTML Expert<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<p>I'm using a <a href="https://getbem.com/">BEM-like</a> naming convention here to indicate that this particular <code><article></code> is a bio. Each of the elements inside the <code><article></code> also has its own class name, indicating its purpose.</p>
<p class="highlight"><strong>Note:</strong> It can be argued that most of these classes are redundant, repeating the information already provided by RDFa. Yet I tend to treat BEM not as a naming convention but as a <a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSL (Domain Specific Language)</a> for creating component-based applications. However, that's a story for another time.</p>
<p>There are also other ways to make HTML more developer-friendly. One of the better (at least in my opinion) is to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements">use custom elements</a> to replace plain old <code><div></code>s:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token comment"><!-- vs --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>the-card</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>the-card</span><span class="token punctuation">></span></span></span></code></pre>
<p>We would also need to add a little bit of CSS:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:not(:defined)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>It ensures that custom elements are displayed as block elements (that is, like <code><div></code>s) because by default, theyāre displayed inline (like <code><span></code>s).</p>
<h2 id="fourth-semantics?">Fourth semantics?</h2>
<p>Yet another elephant looms on the horizon⦠There is no doubt that we live in the AI era. <a href="https://en.wikipedia.org/wiki/Large_language_model">LLMs (Large Language Models)</a> are everywhere around us, and they are yet another kind of machine that can understand HTML. But hey, they are <em>language models</em>, finally our technology has advanced enough to be able to fully grasp the beauty of Web 3.0!</p>
<p>Except it doesn't seem to be the case. Instead of using what we already have, new standards emerge [insert XKCD strip about standards here]. The most popular of them is the <a href="https://llmstxt.org/"><code>/llms.txt</code> file</a>. It's a Markdown file containing detailed instructions for LLMs on how to handle the website and where particular info is located.</p>
<p>Itās quite ironic that after years of striving toward a semantic Web, we finally have <em>the</em> technology capable of understanding it - and it still doesnāt work. Oh, well, maybe in another 20 yearsā¦</p>
<p>However, there is a glimpse of change! OpenAI recently released their AI browser, Atlas. Supposedly, it <a href="https://help.openai.com/en/articles/12627856-publishers-and-developers-faq#h_9910e2a408">understands ARIA</a>. In theory, the more accessible a site is, the more it's suitable for ChatGPT. In practice, it could <a href="https://adrianroselli.com/2025/10/openai-aria-and-seo-making-the-web-worse.html">make the Web far worse</a>. ARIA should be the last resort when <a href="https://w3c.github.io/using-aria/#rule1">native HTML is not enough</a> to express a complex UI pattern. Adding it solely to <em>impress</em> a machine sounds disturbingly similar to an ancient arcane of stuffing web pages with invisible links to fool <a href="https://en.wikipedia.org/wiki/Web_crawler">crawlers</a>. On the other hand, Atlas's reliance on ARIA could be a visible symptom of <a href="https://en.wiktionary.org/wiki/divitis">The Great Disease of the Web</a>: LLMs are powerful enough to understand HTML semantics, but there is no semantics to be understoodā¦</p>
<h2 id="managing-the-menagerie">Managing the menagerie</h2>
<p>Let me ask you a fundamental question: do you pay attention to HTML semantics?</p>
<p>If the answer is "yes", then good job, your elephants are happy and thriving š!</p>
<p>However, if the answer is "no"⦠Don't make them sad, find a little bit of time and enhance your HTML. Throw away extraneous <code><div></code>s, add a couple of headings, embed some metadata! There is no need to do everything at once; do it step by step. Every little improvement matters and can make someone's day a little better. A screenreader user will be grateful for that additional heading, your fellow web developer will thank you for that extra class, and a crawler⦠Well, it won't feel anything, but hey, it will understand your content better!</p>
<p>If you do not know how to start, I suggest removing all distractions ā including CSS ā and focusing on the raw content. Copy it to your favourite word processor and think about the <em>role</em> of each element and its <em>relationship</em> with other elements. And then format all of them according to your findings. That's your content's semantics, waiting to be ported back to HTML!</p>
<p>Do it, for your users, for your fellow web developers (not necessarily for crawlersā¦), but most importantly ā for the <em>elephants</em>!</p>
For the Love of <details>
2025-12-23T00:00:00Z
https://htmhell.dev/adventcalendar/2025/23/
by Justin Ferrell<br><p>When you list all the things HTML can do out of the box without the help of CSS or Javascript, it can seem like a short list. Headings and lists will come to mind. You will most likely think of things like images and video. Any good list of HTML elements will grow to include form elements like input and select, the original interactive elements, too. Recently, a lot of work and attention has turned to some of the lesser known semantic HTML elements too. The work of <a href="https://front-end.social/@heydon">Heydon Pickering</a> comes to mind, and his incredible effort to explain <a href="https://heydonworks.com/article/the-col-element/">every HTML element</a> in alphabetical order.</p>
<p>Among the lists and the images and the forms and the videos though, I think there is an unsung hero. There is a piece of semantic markup that exists at the intersection of SEO, accessibility, performance and interactivity.</p>
<p>I am talking of course about the <details> element and its partner, the <summary> element.</p>
<h2 id="lessdetailsgreater-details"><details> Details</h2>
<p>For the uninitiated, the <details> element acts as a disclosure widget. When I was getting my start in early 2011, the <details> element would have (incorrectly) been called an accordion if it existed at the time. It consists of an outer <details> element with a <summary> element nested directly beneath it. In its āclosedā state, only the <summary> element is visible. When open (with an [open] attribute present on the <details> element) all of the content following the <summary> element is made visible.</p>
<iframe height="300" style="width: 100%;" scrolling="no" title="Basic Demo of <details> " src="https://codepen.io/developerjustin/embed/OPMzVzX?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/developerjustin/pen/OPMzVzX">
Basic Demo of <details> </a> by Justin Ferrell (<a href="https://codepen.io/developerjustin">@developerjustin</a>)
on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>The most remarkable thing about the <details> element is that this disclosure functionality requires no dependencies of any kind. It requires zero CSS or Javascript of any kind, is keyboard navigable and accessible out of the box and is widely understood by search engines and web crawlers alike, making it a semantic choice for things like FAQās.</p>
<p>Put simply, <details> is one of the few semantic, interactive elements HTML gives us. That scarcity makes it special.</p>
<h2 id="lessdetailsgreater-are-always-in-style"><details> Are Always in Style</h2>
<p>If an element gives you so much for free, surely it must fight back when you try to style it, right?</p>
<p>Not the case!</p>
<p>The triangle disclosure icon that you see in a <details> element is actually the list-item marker of the <summary> element. In modern browsers, you can target it with the summary::marker pseudo-element. You can change the size, color, icon and even if the symbol is present at all.</p>
<p>Because the open state of the <details> element is marked using an [open] attribute, itās possible to target the <details> element, the <summary> element and anything therein just by using an attribute selector.</p>
<iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/developerjustin/embed/wBMpayo?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/developerjustin/pen/wBMpayo">
Untitled</a> by Justin Ferrell (<a href="https://codepen.io/developerjustin">@developerjustin</a>)
on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>And using the <strong>::details-content</strong> selector, you can target the inner content of the <details> element too! This <strong>::details-content</strong> selector is particularly powerful when paired with the [open] attribute selector.</p>
<iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/developerjustin/embed/YPqrONy?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true">
See the Pen <a href="https://codepen.io/developerjustin/pen/YPqrONy">
Untitled</a> by Justin Ferrell (<a href="https://codepen.io/developerjustin">@developerjustin</a>)
on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Though browser support isnāt perfect, itās even possible to tweak the animation and transition of a <details> element from its open state to its closed state.</p>
<h2 id="the-devil-and-the-lessdetailsgreater">The Devil and the <details></h2>
<p>Just like its Cascading Counterpart, Javascript is no stranger to the <details> element.</p>
<p>The <details> element fires a toggle event when opened or closed. You can attach an event listener to that just like you would with any other element.</p>
<iframe height="300" style="width: 100%;" scrolling="no" title="Scripted <details> Demo" src="https://codepen.io/developerjustin/embed/dPGJodx?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/developerjustin/pen/dPGJodx">
Scripted <details> Demo</a> by Justin Ferrell (<a href="https://codepen.io/developerjustin">@developerjustin</a>)
on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>To really get at what makes the <details> element tick though, you can always set the open property of the element to true/false to open or close the widget respectively.</p>
<iframe height="300" style="width: 100%;" scrolling="no" title="Toggle <details> Demo" src="https://codepen.io/developerjustin/embed/emJyNMK?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/developerjustin/pen/emJyNMK">
Toggle <details> Demo</a> by Justin Ferrell (<a href="https://codepen.io/developerjustin">@developerjustin</a>)
on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2 id="prevails-through-lessdetailsgreater">Prevails Through <details></h2>
<p>The power of <details> does not stop with how you can style it and how you can interact with it via Javascript. The <details> element has additional capabilities via good old HTML that are arguably more powerful than anything you can do it with Javascript.</p>
<h3 id="deeping-linking">Deeping Linking</h3>
<p>A handy feature of most modern browsers (looking at you, <a href="https://frontendmasters.com/blog/opening-a-details-element-from-the-url/">Safari</a>) is that navigating to an anchor or fragment inside a closed <details> will automatically open it so the user can see the target.</p>
<p>Remarkably, on-page search supports the <details> element too! Browsers support varies but in most modern browsers, traditional "Command/Control + F" search will query the contents of the <details> element and expand them as needed.</p>
<iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/developerjustin/embed/wBMpajg?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/developerjustin/pen/wBMpajg">
Untitled</a> by Justin Ferrell (<a href="https://codepen.io/developerjustin">@developerjustin</a>)
on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h3 id="exclusivity">Exclusivity</h3>
<p>Details elements support a name attribute to group them into an exclusive set (like radio buttons). If multiple <details> share the same name, opening one will automatically close any others in that group.</p>
<iframe height="300" style="width: 100%;" scrolling="no" title="Exclusive <details> Demo" src="https://codepen.io/developerjustin/embed/KwVZpey?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/developerjustin/pen/KwVZpey">
Exclusive <details> Demo</a> by Justin Ferrell (<a href="https://codepen.io/developerjustin">@developerjustin</a>)
on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>It's worth flagging the potential for <a href="https://yatil.net/blog/exclusive-accordions">accessibility issues</a> related to this feature. Exclusive <details> elements are more likely to cause a higher cognitive load and make the comparison of details more difficult. Exclusive <details> elements are by nature more complicated to use with a keyboard and may also cause issues with visibility and screen readers.</p>
<h2 id=""but-her-lessdetailsgreater!"">"But her <details>!"</h2>
<p>In 2025, <details> is well supported across major browsers, though some versions of Safari still have quirks like not auto-opening on anchor links. Itās pretty unlikely that youāll run into meaningful issues but itās always good to see what browsers your users are using.</p>
<h2 id="in-lesssummarygreater">In <summary></h2>
<p>While humble on the surface, the <details> element is an untapped treasure trove of interactivity. It can do so much out of the box but paired with some creative HTML and CSS, itās easy to imagine dozens of uses for the <details> element that do not compromise on performance, accessibility or SEO visibility.</p>
<p>Here are 6 not-so-real world examples with a focus on accessibility and SEO.</p>
<ul>
<li><strong>Frequently Asked Questions (FAQs):</strong> This really is the gold standard for this element. It can help keep pages tidy while ensuring the content is fully indexable by search engines and completely accessible to screen readers without complex ARIA attributes.</li>
<li><strong>Video & Audio Transcripts:</strong> A massive SEO and accessibility win. <details> allows you to include the full text of a podcast or video for keyword indexing and hearing-impaired users, without forcing users to scroll past a massive wall of text to reach the footer.</li>
<li><strong>Product Specification Tables:</strong> <details> allows you to hide dense, technical rows (dimensions, materials, voltages) that are crucial for technical SEO rankings but visually overwhelming for the average shopper.</li>
<li><strong>Table of Contents:</strong> Placing a collapsible "On this page" navigation widget at the top (or sticky side) of long articles helps bots understand page structure and helps users jump to relevant sections immediately.</li>
<li><strong>Long-form Content "TL;DR":</strong> Placing an Executive Summary at the very top of a long article that expands for the full breakdown. This improves "Time on Page" metrics by letting users get the gist immediately rather than bouncing because the article looks too long.</li>
<li><strong>Expanded Author Bios:</strong> Enhance your E-E-A-T (Experience, Expertise, Authoritativeness, and Trustworthiness) signals by including full credentials, past work, and social links in a bio that expands, rather than just a simple name and photo.</li>
</ul>
<p>I hope you find even more uses for <details> in your own work!</p>
The HTML Elements Time Forgot
2025-12-22T00:00:00Z
https://htmhell.dev/adventcalendar/2025/22/
by Declan Chidlow<br><p>Last year I inflicted upon you the cursed knowledge of <a href="https://htmhell.dev/adventcalendar/2024/20/">HTML's legacy colour parsing</a>, a crime for which I'm still yet to pay. This year, I return with more unwanted and unrequested HTML knowledge of yore.</p>
<p>The truth is, HTML is getting old, folks. The initial release was 1993, 32 years ago. It certainly isn't decrepit, but it does have a storied past ā a couple of missteps, tabloid scandals, and unflattering paparazzi photos. That's why I'm so glad you're joining us here today to peruse some of these bad haircuts of HTML's youth.</p>
<p>In the HTML Living Standard there are explicit considerations for what are called <a href="https://html.spec.whatwg.org/multipage/obsolete.html#non-conforming-features">non-conforming features</a>. To quote the standard, <q>Elements in the following list are entirely obsolete, and must not be used by authors</q>. Thus, I shall issue the disclaimer not to use these lest WHATWG show up at my door.</p>
<p><code><marquee></code> is perhaps the most famous of all, second only to <code><blink></code>, which was never standardised. Daniela Kubesch <a href="https://www.htmhell.dev/adventcalendar/2022/15/">wrote about the <code>marquee</code> tag and implementing it in a modern way</a> previously, so I'll leave that to them.</p>
<p>But there are far more obscure tags which are perhaps less visually dazzling but equally or even more interesting. If you're younger, this might very well be your introduction to them. If you're older, this still might be an introduction, but also possibly a trip down memory lane or a flashback to the horrors of the first browser war. It depends.</p>
<h2 id="bgsound">bgsound</h2>
<p><code><bgsound></code> was a way to play sound in the background, because everyone loves web pages making sounds they didn't ask for at them. It was not standardised, and exclusively part of Internet Explorer. The <code><audio></code> tag is the much more courteous modern equivalent.</p>
<p>I find it endlessly humorous that the old HTML Wiki's <a href="https://www.w3.org/html/wiki/Elements/bgsound#Examples">example section for the tag</a> simply states, 'No, really. Don't use it.'. Many of the old and obscure elements share the same detail.</p>
<h2 id="framesets">Framesets</h2>
<p>In the olden days, people used 'frames'. Not iframes mind you, just frames. You can think of their usage vaguely like the HTML imports we have now... Wait, what do you mean we still don't have HTML imports?</p>
<p>I digress. This was a time long before single-page applications (SPAs) or anything of that nature. Frames were widely used as a way to aid navigation without needing to reload the page. You could have a navigation frame that persisted while the content frame changed.</p>
<p><code><frame></code> is more or less the same as our modern <code><iframe></code>, though we use iframes much more for embedding than page layout. <code><frameset></code> holds these frames, so you could write something like <code><frameset cols="50%, 50%"></code> to have two frames next to each other.</p>
<p>The frameset approach had quite a few problems. Accessibility was really poor, and because everything was a single page, it could be hard to link to a specific section. <s>Thankfully we've come a long way since then, and developers take great care with accessibility and ensuring their SPAs respect the web platform.</s></p>
<p>Similarly to how <code><noscript></code> is triggered if JavaScript isn't supported, <code><noframes></code> allows you to set content if frames aren't supported.</p>
<p>While they absolutely shouldn't be used anymore, unlike much of what this article covers, frames and framesets are still supported. They may have been deprecated in HTML5, but the web's backward compatibility keeps them going.</p>
<h2 id="code">Code</h2>
<p><code><pre></code> and <code><code></code> aren't strangers to us. We're good friends, no matter the horrific scripts we may force them to hold. However, pre-<code><pre></code> and <code><code></code> there were plenty of other implementations in the wind.</p>
<p>The e<strong>x</strong>a<strong>mp</strong>le element, <code><xmp></code>, is like our modern <code><pre></code>, but doesn't interpret special characters like <code><</code>, <code>></code>, or <code>&</code>. With <code><pre></code>, we often need to escape these values to avoid Unwanted Shenanigansā¢.</p>
<p><code><listing></code> is yet another step in presenting code on a page. It aimed to display text exactly as the author wrote it, preserving whitespace and formatting, but never got proper support and became obsolete with HTML5. Modern browsers handle it the same as <code><pre></code>.</p>
<p><code><plaintext></code> is a funny one. It does what it says on the tin ā that is, tells the browser to interpret everything after it as plain text. Of course, this means that any potential closing tag is <em>also</em> interpreted as plain text and therefore not parsed by the browser. Once opened, you cannot close a <code><plaintext></code>. The rest of the file becomes plaintext for eternity and whatever comes after. It was deprecated in HTML 2 and made invalid in HTML 4, so most modern browsers just interpret it the same as a <code><pre></code>. The modern approach is to serve content with a MIME type of <code>text/plain</code>.</p>
<h2 id="spacer">spacer</h2>
<p>Before the modern days of flexbox and grid and even floats, we laid things out with tables. Like cavepeople.</p>
<p>Of course, sometimes with tables, you need a spacer. <code>spacer.gif</code> used to be popular ā a simple 1px Ć 1px image that could be used to fill a space. But then Netscape got fancy with it and introduced the totally tubular <code><spacer></code> tag. You could write <code><spacer width="50" height="20"></code> on your website and get a little element that took up some space.</p>
<p>Pretty fly, though this was only a stop-gap implementation that didn't last long and was never standardised.</p>
<h2 id="keygen">keygen</h2>
<p>I hear keygen, and my mind goes to acquiring a copy of a video game or enterprise software in a less than scrupulous way. That isn't to say the <code><keygen></code> element had anything to do with software cracking, though, and unfortunately didn't come with <a href="https://www.youtube.com/watch?v=0emL2ACcBkk">sick tunes</a>.</p>
<p>It was a way for browsers to generate public-private key pairs for form submissions, primarily used for certificate generation. The idea was to make cryptographic operations easier for users, but in practice, it had security concerns and inconsistent browser support. You can read a little bit of discussion from the time in <a href="https://lists.whatwg.org/pipermail/whatwg-whatwg.org/attachments/20080714/07ea5534/attachment.txt">this mailing list thread</a>. It was deprecated and is now removed from all modern browsers.</p>
<h2 id="nextid">nextid</h2>
<p><code><nextid></code> comes with the fun trivia of being the first tag to be eliminated from the official public DTD's of HTML versions. It was used by early HTML editors to generate unique identifiers but quickly became obsolete. It was part of those early days of HTML where the vision wasn't just viewing but also editing.</p>
<p>It is a hard element to get much info about, and it seems I'm not alone in thinking that, as the very interesting and well-worth-a-read document <a href="http://the-pope.com/nextid.html">Working NEXTID Tag Element Example</a> has this to say:</p>
<blockquote>
<p>It is also probably one of the least understood of all of the early HTML elements, being poorly documented, not explained in any depth anywhere, and those who obviously understood how it worked couldn't be bothered to explain it to the rest of us.</p>
</blockquote>
<h2 id="isindex">isindex</h2>
<p><code><isindex></code> is a particularly interesting part of the old web because, perhaps more than any other tag shown here, it reflects a very different vision of HTML semantics. It was a way to define a search box that would submit queries directly to the server. You'd write something like:</p>
<p><code><isindex prompt="Enter your search term here:"></code></p>
<p>This primitive search functionality was eventually superseded by proper form elements, giving developers much more control over how search interfaces worked.</p>
<h2 id="dir">dir</h2>
<p>I'm talking about <code><dir></code>, not <code>dir</code> ā the element, not the property indicating text direction. The purpose of <code><dir></code> was to serve as a container for files and directories. It wasn't just your bog-standard list, though, because the user agent could apply different styles and icons depending on the content type.</p>
<p>You could also give it the attribute <code>compact</code> to make the content take up less space in an effect similar to setting <code>line-height</code>. As you might imagine, the modern equivalent is to use a standard list with <code><ul></code> or <code><ol></code>.</p>
<h2 id="closing-tags">Closing Tags</h2>
<p>It is easy for us to look back from the year of our lord <code title="Don't you dare send me an email about this.">${new Date().getFullYear()}</code> and ridicule what obviously wasn't to be, but these stumblings have made HTML what it is today. No, they weren't perfect, but it is mistakes that we learn from.</p>
<p>These obsolete tags represent experiments in web developmentāsome more successful than others. They're a reminder that the web platform we use today is the result of decades of trial, error, and evolution. And who knows? Maybe someday developers will look back at our current HTML practices with the same bemused affection.</p>
<p>We often think of the web as forever backwards compatible and never breaking, but that plainly isn't the case. As we've seen, there are plenty of elements that have been left behind in the march of progress. The web evolves, standards change, and what was once cutting-edge becomes a curious footnote in the platform's history.</p>
Styling by Language: Using the lang Attribute for Multilingual Design
2025-12-21T00:00:00Z
https://htmhell.dev/adventcalendar/2025/21/
by Julia Undeutsch<br><style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100..900&family=Noto+Sans:ital,wght@0,100..900;1,100..900&family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&display=swap');
:lang(en) {
font-family: "Nunito Sans", sans-serif;
}
:lang(ja) {
font-family: "Noto Sans JP", sans-serif;
}
p.adjust:lang(ja) {
font-weight: 300;
}
span.adjust:lang(en) {
font-weight: 400;
}
h2.adjust:lang(ja) {
font-weight: 500;
letter-spacing: 0.05em;
}
h2.adjust span:lang(en) {
font-family: "Noto Sans", sans-serif;
font-size: 1.06em;
letter-spacing: 0;
vertical-align: -0.03em;
}
</style>
<h2 id="when-east-meets-west-(on-the-same-page)">When East Meets West (on the Same Page)</h2>
<p>If youāve ever built a bilingual English ā Japanese website, you know the struggle. English uses letters with ascenders, descenders, and varying widths. Japanese, on the other hand, mixes three scripts: <a href="https://en.wikipedia.org/wiki/Kanji">kanji</a>, <a href="https://en.wikipedia.org/wiki/Hiragana">hiragana</a>, and <a href="https://en.wikipedia.org/wiki/Katakana">katakana</a>, each forming balanced, square-like characters.</p>
<p>Already last year, I told you about a special HTML element that you can use to style this scripts. Read my post <a href="https://www.htmhell.dev/adventcalendar/2024/12/">HTML and CSS I didn't even know about before I started creating content</a>.</p>
<p>This is not usually a big problem if the entire page is in one language. But when Latin letters are mixed with Japanese characters, as is increasingly the case, the font becomes more difficult to handle.</p>
<p>That means:</p>
<ul>
<li>Japanese text usually needs more line height to breathe.</li>
<li>Kanji often appear denser and more detailed than Latin letters.</li>
<li>Western fonts can feel too ālightā next to Japanese text.</li>
</ul>
<p>This is where the <code>lang</code> attribute becomes your best friend. And he brings a plus one: CSS.</p>
<h2 id="let-the-language-drive-the-design">Let the Language Drive the Design</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Welcome to Starbucks.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ja<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ć¹ćæć¼ćććÆć¹ćøććććć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:lang(en)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">"Nunito Sans"</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">:lang(ja)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">"Noto Sans JP"</span> sans-serif<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p lang="en">Welcome to Starbucks.</p>
<p lang="ja">ć¹ćæć¼ćććÆć¹ćøććććć</p>
<p>Here, the English text uses <strong>Nunito</strong>, which has friendly, rounded shapes. The Japanese version pairs quite good with <strong>Noto Sans JP (Google Fonts)</strong>, which was designed specifically as a typeface for all writing systems around the world. Since the content is separate, both fonts appear suitable and visually balanced.<br />
Read more about the <a href="https://fonts.google.com/noto/specimen/Noto+Sans+JP">Noto: A typeface for the world</a> project.</p>
<h2 id="font-weight-and-visual-balance">Font Weight and Visual Balance</h2>
<p>However, when both writing systems are mixed together, which is common in the Japanese language today, typographical mismatches become noticeable. Meaning, When mixing languages, a font weight of <code>400</code> for Japanese might look visually heavier than <code>400</code> in Latin text because kana systems are more complex in shape.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ja<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Starbucks<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>ćøććććć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p lang="ja"><span lang="en">Starbucks</span>ćøććććć</p>
<p>You can correct this by slightly adjusting weights between languages:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:lang(en)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-weight</span><span class="token punctuation">:</span> 400<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">:lang(ja)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-weight</span><span class="token punctuation">:</span> 300<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p lang="ja" class="adjust"><span lang="en" class="adjust">Starbucks</span>ćøććććć</p>
<p>This subtle change keeps both scripts visually balanced without the Japanese text looking darker. But if you look more closely and highlight the text, you will notice that the default line height of the whole text is slightly different, due to the different fonts used. And the whole thing becomes even more challenging when kanji characters are mixed in with the text.</p>
<h2 id="kanji-density-and-design">Kanji: Density and Design</h2>
<p>Kanji are logographic charactersāeach one carries meaning, not just sound. Because theyāre compact but information-dense, large blocks of kanji can look heavier.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Apple unveils the new iPhone 17 Pro.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ja<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>ćę°å<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>iPhone 17 Pro<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>ćēŗč”Øć</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span></code></pre>
<h2 lang="en">Apple unveils the new iPhone 17 Pro.
<h2 lang="ja">
<span lang="en">Apple</span>ćę°å<span lang="en">iPhone 17 Pro</span>ćēŗč”Øć
</h2>
<p>You can help readability with</p>
<ul>
<li>generous <code>line-height</code> (1.7ā1.9)</li>
<li>slightly looser letter spacing (<code>letter-spacing: 0.05em</code>)</li>
<li>a not too heavy <code>font-weight</code></li>
<li>avoiding <code>all-caps</code> Latin text near kanji ā caps feel bulky by comparison</li>
</ul>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:lang(ja)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">"Noto Sans JP"</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">font-weight</span><span class="token punctuation">:</span> 500<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">letter-spacing</span><span class="token punctuation">:</span> 0.05em<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">:lang(en)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">"Noto Sans"</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 1.06em<span class="token punctuation">;</span> <span class="token comment">/* +5% larger than Japanese */</span></span><br /><span class="highlight-line"> <span class="token property">letter-spacing</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">vertical-align</span><span class="token punctuation">:</span> -0.03em<span class="token punctuation">;</span> <span class="token comment">/* align baseline slightly */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h2 lang="ja" class="adjust"><span lang="en">Apple</span>ćę°å<span lang="en">iPhone 17 Pro</span>ćēŗč”Øć</h2>
<p>When styled this way, the English and Japanese text sit naturally on the same line ā adjusting the English spans with a slightly larger font size and a small <code>vertical-align</code> nudge raises their baseline just enough to match the Japanese characters. The result feels balanced in both weight and rhythm ā one cohesive headline, not two scripts stitched together.</p>
<p class="highlight"><strong>Bonus Tip: Variable Fonts!</strong> Googleās <a href="https://fonts.google.com/knowledge/glossary/variable_fonts">Variable Fonts</a> make it even easier to fine-tune weight and width dynamically between scripts. If you use <code><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings">font-variation-settings</a></code>, you can ensure text looks balanced across both languages at every viewport size.</p>
<h2 id="takeaway">Takeaway</h2>
<p>Multilingual design isnāt just about translation ā itās about typographic empathy. English and Japanese each have their own rhythm, density, and shape language. Using the <code>lang</code> attribute with the right fonts helps you respect both, creating an experience that feels natural to all readers.</p>
<p>Next time you use different scripts on your page, donāt just think about switching words. Think about switching <em>style</em>, and let your typography speak both languages fluently.</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://fonts.google.com/noto">Noto: A typeface for the world</a></li>
<li><a href="https://fonts.google.com/knowledge/glossary/variable_fonts">Variable Fonts</a></li>
</ul>
</h2>
The many lives of the page title
2025-12-20T00:00:00Z
https://htmhell.dev/adventcalendar/2025/20/
by Katrin Kampfrath<br><p>The page title ā together with its co-star, the favicon ā appears on stage in the browser tab. It shows up in search results. Itās announced by screen readers when a page loads. And it even becomes the default name when you bookmark a page.</p>
<p>It lives in the <code><title></code> tag within in the <code><head></code> of the document, supposedly unseen and often overlooked. Most tips and tricks around the page title focus on SEO benefits. But thatās not all: it also plays a vital role in the user flow of navigating the web.</p>
<h2 id="the-user-experience-of-a-page-title">The user experience of a page title</h2>
<p>Clicking a link and having a new page load is the defining usage pattern of the world wide web. How do we know we clicked the right link? How do we know to which tab to return to when we switched to another program?</p>
<p>For screen reader users the page title is among the first things announced. On page load, sighted users will most likely first notice the H1 heading. When coming from another application or browser tab, the page title helps us identify the right window or browser tab. A page title identifies or describes a page and as such reassures us that we are indeed on the chosen page.</p>
<h2 id="the-page-title-in-complex-user-flows">The page title in complex user flows</h2>
<p>One overlooked aspect of the page title is its use in more complex user flows, for example a multi-step form split across several screens.</p>
<p>I specifically use the term "screen" here ā multi-step user flows are often built with JavaScript as multiple screens on one route (effectively, under one URL). From a developer's perspective, it is just one page. From a user's perspective, however, these are multiple pages.</p>
<p>Let's consider a few multi-step user flows with potential page titles:</p>
<ul>
<li>A <strong>multi-step form</strong> is a very common user flow, such as a checkout process or an application form. A typical page title for all screens is "Checkout - site name". A more contextual title would be "Payment details - Checkout - site name".</li>
<li><strong>Search results</strong> that are placed across several pages. A typical page title for all screens is "Search results - site name". A more contextual title would be "17 search results for {search term} - site name".</li>
<li>An <strong>online learning platform</strong> where each chapter consists of the learning content and related exercises. A typical page title for all screens is "{Name of chapter} - site name". A more precise title would be "Page 1 - {Name of chapter} - site name", "Exercise 1 - {Name of chapter} - site name", etc.</li>
<li><strong>Profile pages</strong> with several settings tabs for appearance, notifications, activity and so on. A typical page title for all screens is "Profile - site name". A more precise title for each screen would be "Appearance - Profile ā site name", "Notifications ā Profile ā site name", and so on.</li>
</ul>
<p>In all examples, users benefit from a unique page title per screen, describing the specific content of what is currently displayed on the page.</p>
<h3 id="example-a-multi-step-form">Example: A multi-step form</h3>
<p>Let us dive deeper with the example of a multi-step form. Say we're buying a bike insurance. First, we enter our name and address. Then the bike details, such as purchase price, serial number, and whether it's an e-bike. Then payment details. The design is one screen per block of information, with no other main content.</p>
<img src="https://htmhell.dev/adventcalendar/2025/20/bike-insurance.png" width="740" height="569" loading="lazy" alt="Browser tab for entering the bike details mentioned in the previous paragraph. In the center there are the form fields, on the left side is a navigation for the multiple steps, the browser tab shows the page title 'Buy bike insurance step 2'." />
<p>The user navigates through the form. They enter their name and address, click āNext,ā and move on to the bike details. Then they realise they donāt remember the exact purchase price. They open their email app to check ā and while they're there, another email catches their eye. They click a link, open another tab, and get distracted.</p>
<p>Returning to the insurance form should be as seamless as possible. Users likely scan all browser tabs first to find the right one. Clear page titles make it easy to identify where they left off.</p>
<p>Consider these two possible titles:</p>
<ol>
<li>"Buy bike insurance - Company Name"</li>
<li>"Bike details - Step 2 of 4 Ā· Buy bike insurance - Company Name"</li>
</ol>
<p>The first one identifies the form, but doesnāt reinforce the user's progress. The second one stells the user exactly where they are in the process.</p>
<h2 id="relevant-wcag-requirements">Relevant WCAG requirements</h2>
<p>So far, weāve looked at this from a user-experience point of view. Next, we look at what is needed from an accessibility compliance perspective.</p>
<p>In the Web Content Accessibility Guidelines (WCAG) there are two success criteria related to our topic:</p>
<ol>
<li><a href="https://www.w3.org/TR/WCAG22/#page-titled">Success Criterion 2.4.2 Page Titled</a> checks whether a page title describes the topic or purpose of the current page.</li>
<li><a href="https://www.w3.org/TR/WCAG22/#link-purpose-in-context">Success Criterion 2.4.4 Link Purpose (In Context)</a> concerns the links on a page.</li>
</ol>
<p>With regard to accessibility and legal requirements, the WCAG (also included in the European standard EN 301549) states minimum requirements. Think of text-to-background color contrast: the ratio of 4.5:1 is the minimum, but stronger contrast is always better.</p>
<p>Going back to our bike insurance example, this is how we would audit this process at our company.</p>
<p>For Success Criterion 2.4.2 Page Titled we check:</p>
<ol>
<li><strong>Does each step have a unique URL?</strong> Then the page title needs to mention the specific topic of the current page in the page title, such as "Bike details" or "Bike details step 2 of 4".</li>
<li><strong>If there are no unique URLs</strong> (i.e. the form always starts at step 1), the overall form process must be sufficiently reflected in the page title. "Buy bike insurance - company name" would pass the success criterion. In an audit, we would then add a recommendation to include the current step for user-experience reasons.</li>
</ol>
<p>For Success Criterion 2.4.4 Link Purpose (In Context), things are a little less clear. The main intent of the success criterion is that "<a href="https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html">users understand what each link will do</a>." The criterion focuses on the "read a link" part, so an accessibility audit will check the link texts on a given page to see if all links on one page are distinguishable from each other. This aspect is the minimum requirement.</p>
<p>Nonetheless, the user flow still plays a role. Recall the typical flow: read a link, click a link, have a new page load. In the WCAG understanding document for this criterion, it says:</p>
<blockquote>
<p>Having the link and the title agree, or be very similar, is good practice and provides continuity between the link 'clicked on' and the web page that the user lands on.</p>
</blockquote>
<p>Adding context about the current step in a flow or the current page in a set of pages helps all users stay oriented.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Give your page sufficiently unique page titles. What is sufficient depends on the implementation and design details of your website.</p>
<p>Keep the user flow in mind. The page title is a key element to reassure users they are indeed on the page they intended to be.</p>
<p>Developer experience is not the same as user experience. As developers we know how things work on a technical level, and we might easily forget how users perceive a website. Never assume people will visit your site only in one straightforward session with no distractions. Clear, contextual titles help people stay oriented when they return.</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://www.w3.org/TR/WCAG22/">Web Content Accessibility Guidelines (WCAG) 2.2</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/title">MDN ā Document.title property</a></li>
<li><a href="https://accessibility-cookbook.com/">Manuel MatuzoviÄ ā Web Accessibility Cookbook</a>, chapter "1.2 Describe the Document".</li>
<li><a href="https://www.smashingmagazine.com/printed-books/inclusive-front-end-design-patterns/">Heydon Pickering ā Inclusive Design Patterns</a>, chapter "The <title> Element".</li>
<li><a href="https://hidde.blog/accessible-page-titles-in-a-single-page-app/">Hidde de Vries ā "Accessible page titles in a Single Page App"</a></li>
<li><a href="https://dequeuniversity.com/checklists/web/page-title">Deque University ā Page Title Checklist</a></li>
</ul>
Semantics beyond the tag name
2025-12-19T00:00:00Z
https://htmhell.dev/adventcalendar/2025/19/
by Nathan Knowler<br><p>There is a terrible epidemic that plagues the web: <i>divitis</i>.</p>
<p>If youāre unfamiliar, <dfn><i>divitis</i></dfn> is a condition where a website uses an unholy amount of <code><div></code> elements. Some of the worst cases even include <code><div></code>s used as buttonsā<em>blasphemy!</em></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>site-header<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>site-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>divs r us<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>navigation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>link<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>my favourite divs<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>link<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>moor divs<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>main<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>container-inner<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>card-inner<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>card-header<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>i love div<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>all my friends are divs<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>card-footer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>accept div as your lord and saviour<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>site-footer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>div-webring<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>join the divitis webring<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button next<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>next<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button prev<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>previous<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>To avoid <i>divitis</i>, authors are often encouraged to use <i>semantic HTML</i>. As it turns out, HTML has over a hundred elements that are not named <code>div</code> or <code>span</code>. Semantic HTML elements describe what their content is.</p>
<p>Inspired by this newfound responsibility, authors open the lexicon of HTML elements and begin to compose⦠<em>absolute gibberish.</em></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>my-card</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>divs suck<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>and so do classes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span>who needs classes when you have semantic HTML. combine elements however youād like to explore new possibilities.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/hell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span>I have no class<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>my-card</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>my-card</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>replace all your divs with sections<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>theyāre so semantic<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span>whenever I donāt know what element to choose, section is there for me. section is my best friend forever.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/hell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span>become semantic with sections<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>my-card</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now, this might not seem like gibberish on the surfaceāit might actually seem like it makes a lot of sense. The problems will become more apparent throughout this post. We cannot simply use HTML elements based on their tag names and expect them to translate to anything actually meaningful.</p>
<p>To be fair, this is to be expected of anyone learning a new language. However, a problem on the web is that authors delude themselves into thinking that they are āwriting semantic HTML,ā therefore gaining all of the benefits that are understood to come with that like accessibility, and they never advance past that stage.</p>
<p>Itās like when my own child confidently declares, after learning a handful of words in Japanese, āI know Japanese.ā Itās cute and Iām not going to argue with it, because theyāre a child.</p>
<p>Youāre likely not a child though, so I will tell it to you straight: <em>this is not cute.</em></p>
<p>Once youāve recovered from that devastating realization, we can proceed.</p>
<p>To actually write semantic HTML, we need to know what elements mean beyond just what we infer from their tag names and how to use them. So, how do we find that out?</p>
<h2 id="where-html-semantics-come-from">Where HTML semantics come from</h2>
<p><a href="https://html.spec.whatwg.org/multipage/">The HTML standard</a> is what defines HTML elements. For example, we can find the definition of the <code><main></code> element in <a href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-main-element">section 4.4.14</a>:</p>
<blockquote>
<p>The <code>main</code> element represents the dominant contents of the document.</p>
</blockquote>
<p>Beyond the definition, the standard also includes rules and guidance for usage:</p>
<blockquote>
<p>A document must not have more than one <code>main</code> element that does not have the <code>hidden</code> attribute specified.</p>
</blockquote>
<blockquote>
<p>A <dfn>hierarchically correct <code>main</code> element</dfn> is one whose ancestor elements are limited to <code>html</code>, <code>body</code>, <code>div</code>, <code>form</code> without an accessible name, and autonomous custom elements. Each <code>main</code> element must be a hierarchically correct <code>main</code> element.</p>
</blockquote>
<p>From these descriptions, we can gather that using the <code><main></code> element for the main content of a card component, which can be used multiple times like the ones in the earlier āgibberishā example, would be incorrect:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>my-card</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span><span class="token comment"><!-- ⦠--></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span><span class="token comment"><!-- first card main content --></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span><span class="token comment"><!-- ⦠--></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>my-card</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>my-card</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span><span class="token comment"><!-- ⦠--></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span><span class="token comment"><!-- second card main content --></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span><span class="token comment"><!-- ⦠--></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>my-card</span><span class="token punctuation">></span></span></span></code></pre>
<p>Tag names are not enough for us to understand what an element is for. We need to adopt HTMLās own definitions for semantic elements.</p>
<h3 id="why-follow-the-html-standard?">Why follow the HTML Standard?</h3>
<p>Iām not your god, and neither is the HTML Standardābut if you choose to use HTML elements for how you perceive them, things break in ways you canāt control. Assistive tech, legal requirements, user expectations... it all hinges on the platformās actual definitions. Ignore them and, well, your documents might just end up <a href="https://www.htmhell.dev/">in hell</a>.</p>
<h3 id="but-what-about-aria?-cant-i-just-use-that-instead?">But what about ARIA? Canāt I just use that instead?</h3>
<p><a href="https://w3c.github.io/aria/">Accessible Rich Internet Applications</a>, more commonly known by the acronym ARIA, is a set of roles and attributes that can be used in HTML to add accessibility related semantics to web content.</p>
<p>It can be tempting to think that we donāt need semantic HTML, instead we can just sprinkle our <i>divitis</i> with <em>holy</em> ARIA roles and attributes. Well, while this can improve the accessibility of that hoard of elements, weāre likely breaking <a href="https://www.w3.org/TR/using-aria/#firstrule">the first rule of ARIA usage</a>: donāt use ARIA if you donāt need to.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>button</span><span class="token punctuation">></span></span>accept div as your lord and saviour<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Many semantic HTML elements have whatās called <i>implicit ARIA semantics</i>. These are ARIA roles and properties that the browser has mapped for us. Instead of needing to explicitly write out the ARIA semantics for an element each time you use it, we can use semantic HTML elements as a shorthand for these.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>This machine kills fascists<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Beyond simply being a shorthand for a set of ARIA semantics, built-in HTML elements also include the necessary functionality and presentation that would be expected of an element bearing such semantics. In many cases, this means that you donāt need to write any JavaScript for your HTML to work as expected. Using JavaScript in lieu of built-in HTML functionality is wasteful and error prone, degrading the usability of your website.</p>
<p>Now that we understand the folly of ignoring the first rule of ARIA usage, letās turn our attention to <i>implicit ARIA semantics</i> and how they work in semantic HTML.</p>
<h2 id="the-implicit-aria-semantics-of-html">The implicit ARIA semantics of HTML</h2>
<p>Along with the definitions for each element, the HTML standard includes accessibility considerations for both authors (i.e. you and I) and implementors (i.e. the people implementing the elements in browsers). The linked document for authors is called <a href="https://w3c.github.io/html-aria/">āARIA in HTMLā</a> and the one for implementors is called <a href="https://w3c.github.io/html-aam/">āHTML Accessibility API Mappingsā (HTML-AAM)</a>. The each of these links will jump to the relevant bits for the element in question.</p>
<p>Both of these include what the implicit ARIA semantics are for HTML elements and attributes. āARIA in HTMLā is more geared towards authors: it includes both rules and recommendations for ARIA attribute usage with HTML. HTML-AAM includes specifics of how elements should be mapped to various accessibility APIs including, but not limited to, ARIA.</p>
<p>Turning again to the <code><main></code> element, we can see that <a href="https://w3c.github.io/html-aria/#el-main">it has an implicit ARIA role of <code>main</code></a>.</p>
<p>If we look at heading elements like <code><h1></code> or <code><h2></code>, we see that <a href="https://w3c.github.io/html-aria/#el-h1-h6">they have an implicit ARIA role of <code>heading</code> along with an implicit <code>aria-level</code> of whatever number is a part of the tag name</a> (i.e. HTML only includes six heading levels).</p>
<p>Beyond element tag names, there are three details I want to point out in these documents:</p>
<ol>
<li>Both an elementās attributes and its context can effect its implicit ARIA semantics.</li>
<li>Some elements do not have implied semantics unless they are named.</li>
<li>Not all elements have significant implicit ARIA semantics. Thatās not to say they never will, but this gives us a good idea of where we can prioritize our time.</li>
</ol>
<p>Letās dig into some examples.</p>
<h3 id="attributes-affect-implicit-semantics">Attributes affect implicit semantics</h3>
<p>Consider the following elements:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span><span class="token punctuation">></span></span>Anchor<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>https://example.com</span><span class="token punctuation">></span></span>Anchor with an <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>code</span><span class="token punctuation">></span></span>href<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>code</span><span class="token punctuation">></span></span> attribute<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>Are these both links?</p>
<p>No, they arenāt. The first is just an anchor element. Itās implicit ARIA role is <code>generic</code> which is not interesting for accessibility APIs. CSS also doesnāt even consider it a link as it will not match the <code>:any-link</code> or <code>:link</code> pseudo-class selectors.</p>
<p>Since the latter anchor element has an <code>href</code> attribute, it is considered a link, and therefore has the implicit ARIA role of <code>link</code> and can be matched by the above mentioned semantic pseudo-class selectors with CSS.</p>
<h3 id="naming-affects-implicit-semantics">Naming affects implicit semantics</h3>
<p>A popular favourite among those first starting out with semantic HTML is the <code><section></code> element. What is often never realized is that it has no valuable ARIA semantics if it is not named (i.e. its implicit role is <code>generic</code>). This makes it not much different than using a <code><div></code> (i.e. its implicit role is also <code>generic</code>).</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Insignificant section<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>section-label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>section-label</span><span class="token punctuation">></span></span>Significant document region<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>With that said, please do not go name <em>all</em> of your <code><section></code> elements!</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>section-1-label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>section-1-label</span><span class="token punctuation">></span></span>Top one hundred reason I love sections<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>section-2-label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>section-2-label</span><span class="token punctuation">></span></span>Section is whatever I want it to be<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>section-3-label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>section-3-label</span><span class="token punctuation">></span></span>Section is my friend<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>A labelled <code><section></code> element will have <a href="https://w3c.github.io/aria/#region">the <code>region</code> ARIA role</a>. This is whatās called a <dfn><i>landmark</i></dfn>. <a href="https://tetralogical.com/blog/2022/03/18/landmarks/">Landmarks</a> are important sections of a document that a user might benefit from easy access to.</p>
<p>The problem here is that everything is a landmark, then nothing is a landmark.</p>
<p>It can be easy to think that labelling your <code><section></code> elements satisfies the requirement for using them, but that is not the case here. Both the meaning of the tag name and the HTML definition of a <code><section></code> element is way more generic than the <code>region</code> ARIA role. Before you label your <code><section></code> elements ensure they make sense as regions, otherwise, youāll likely be making it more difficult to find whatās important on a page. If youāre already using <a href="https://tetralogical.com/blog/2022/02/28/headings/">a good heading structure</a> throughout your document, that is good enough for what youāre likely trying to do.</p>
<h3 id="context-affects-implicit-semantics">Context affects implicit semantics</h3>
<p>HTML elements can have different implicit ARIA semantics when used in different contexts.</p>
<p>For example, the <code><header></code> and <code><footer></code> elements have an important meaning for documents when used outside of the <code><main></code> element or any other sectioning content element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span>Header content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>Main content of the document<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span>Footer content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span></code></pre>
<p>In this scenario, the <code><header></code> elementās implicit ARIA role is <code>banner</code> and the <code><footer></code> elementās implicit ARIA role is <code>contentinfo</code>. Documents should only have one of each of these landmarks.</p>
<p>That does not mean that you cannot use either of these elements in other places within your document. When we review <a href="https://html.spec.whatwg.org/multipage/sections.html#the-header-element">the definition of a <code><header></code> element</a> it says:</p>
<blockquote>
<p>āThe <code>header</code> element represents a group of introductory or navigational aids.ā</p>
</blockquote>
<p>That implies much broader usage than <a href="https://w3c.github.io/aria/#banner">the definition of the <code>banner</code> role</a>:</p>
<blockquote>
<p>āA <code>landmark</code> that contains mostly site-oriented content, rather than page-specific content.ā</p>
</blockquote>
<p>Until 2025, when used inside of the <code><main></code> element or <a href="https://html.spec.whatwg.org/multipage/dom.html#sectioning-content">sectioning content</a>, <code><header></code> and <code><footer></code> elements were mapped with a <code>generic</code> role which means they didnāt have any value for accessibility purposes. After relatively recent changes to the latest ARIA spec and HTML-AAM drafts, in these contexts they have been mapped to new ARIA roles: <a href="https://w3c.github.io/aria/#sectionheader"><code>sectionheader</code></a> and <a href="https://w3c.github.io/aria/#sectionfooter"><code>sectionfooter</code></a>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span>banner landmark role<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span>sectionheader role<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Article content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span>sectionfooter role<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span>contentinfo landmark role<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span></code></pre>
<p>As of writing, these changes have been implemented in Chromium-based browsers and Safari. <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1893684">Firefox has yet to implement them</a>. With that said, as these roles are new, the support from assistive technologies will likely be non-existent or quite limited.</p>
<p>The <code><header></code> and <code><footer></code> elements are examples of context being an important factor for implicit ARIA semantics. At first, only specific usage was used for setting the landmark roles. Now, later on, the broader usage allowed of these elements in other contexts can be exposed semantically.</p>
<h3 id="a-combinations-of-these-factors-affect-implicit-semantics">A combinations of these factors affect implicit semantics</h3>
<p>Sometimes an elementās implicit semantics are informed by a combination of the factors that weāve explored so far. Consider the <code><aside></code> element.</p>
<p>In the following situations, the <code><aside></code> elementās implicit role is <code>complementary</code> (a landmark) because itās scoped to the <code><body></code> element or the <code><main></code> element:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Main content.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Complementary to the main content, but standalone.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Main content</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Complementary to the main content, but standalone.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span></code></pre>
<p>If you mark up your blog posts with an <code><article></code> element which is sectioning content, then unnamed <code><aside></code> elements have the <code>generic</code> role.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Some article<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Some content, blah, blah, blahā¦</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Failed attempted at complementary.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<p>The only way to make this element have the <code>complementary</code> role is to use <code>aria-labelledby</code> or <code>aria-label</code>. That could be a challenge if the content has no heading. You may need to be creative about wrapping part of the content to use as a label or introduce a visually hidden label if you want to avoid some of <a href="https://adrianroselli.com/2019/11/aria-label-does-not-translate.html">the translation pitfalls of <code>aria-label</code></a>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Some article<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Some content, blah, blah, blahā¦</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>complementary-label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>complementary-label</span><span class="token punctuation">></span></span>Failed attempt<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> at complementary.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<p>All of this is not something someone could have inferred from the HTML standard alone. This is why documents like <a href="https://w3c.github.io/html-aria/">āARIA in HTMLā</a> and <a href="https://w3c.github.io/html-aam/">HTML-AAM</a> are invaluable for good semantic usage.</p>
<h3 id="custom-elements-can-have-implicit-semantics-too">Custom elements can have implicit semantics too</h3>
<p><a href="https://html.spec.whatwg.org/multipage/custom-elements.html">Custom elements</a> are an HTML feature that allow authors to define their own HTML elements. A tag name can be defined as a custom element using the <code>window.customElements.define()</code> method in JavaScript. Like built-in HTML elements, custom elements can have their own implicit ARIA semantics. This is set using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals"><code>ElementsInternals</code></a> API.</p>
<p>Here is an example of defining a custom element and setting an implicit ARIA role for it:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"> <span class="token keyword">class</span> <span class="token class-name">HellButtonElement</span> <span class="token keyword">extends</span> <span class="token class-name">HTMLElement</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token keyword">const</span> internals <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">attachInternals</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> internals<span class="token punctuation">.</span>role <span class="token operator">=</span> <span class="token string">"button"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Continue implementing button functionality š</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">"not-a-div"</span><span class="token punctuation">,</span> HellButtonElement<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>not-a-div</span><span class="token punctuation">></span></span>A div is not a button<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>not-a-div</span><span class="token punctuation">></span></span></span></code></pre>
<p>When you create custom elements, you become responsible for communicating their purpose to other authors. This should also include any implicit semantics youāve set, as well as, what you consider good authoring practice to be when using them.</p>
<p>I highly recommend using HTML itself as a model for how you build custom elements. In many ways, that might mean thinking less like an author and more like an implementor or a specification writer. āHow do I create elements for others to use?ā Implicit semantics are an important part of that because they allow you to <a href="https://www.w3.org/TR/design-principles/#priority-of-constituencies">put other authors first</a>.</p>
<h2 id="test-test-test">Test, test, test</h2>
<p>In the same way that you can spend a whole lot of time writing valid, semantic HTML that has no implicit ARIA semantics, you can also spend a whole lot of time on writing HTML according to the specifications that actually isnāt implemented in any browser or that is implemented incorrectly in browsers. This is why testing is vital.</p>
<p>Testing might look like checking what ARIA roles and properties are computed for elements in a browserās developer tools (<a href="https://devtoolstips.org/tips/en/see-accessibility-tree/">use the accessibility tree in Chrome/Edge and Firefox</a> or <a href="https://webkit.org/web-inspector/elements-tab/#node-panel">Accessibility section of the Node Panel in Safariās Web Inspector</a>). It also might look like actually using assistive technologies such as screen readers or voice control. It is very helpful to understand how assistive technology users experience various elements and so seeking real feedback is invaluable to the testing process. Just make sure to show anyone you do involve that you value their time and labour.</p>
<p>Testing helps us understand what is valuable and what we should focus on. I can spend a whole lot of time trying to perfectly mark up all of my words using <a href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#text-level-semantics">text-level semantics</a>, but then realize through testing that Iāve delivered little to no semantic value to assistive technology users.</p>
<p>Courses such as <a href="https://practical-accessibility.today/">Sara Soueidanās Practical Accessibility</a> or <a href="https://testingaccessibility.com/">Marcy Suttonās Testing Accessibility</a> are great resources for those wanting to get started with testing various assistive technologies.</p>
<h2 id="go-forth-and-actually-write-semantic-html">Go forth and actually write semantic HTML</h2>
<p>Weāve learned that:</p>
<ol>
<li>HTML tag names are not a reliable way of understanding what an element is for.</li>
<li>The HTML standard defines what semantic HTML elements are for.</li>
<li>Some semantic HTML elements also have implicit ARIA semantics, and in some cases that depends on what attributes are set, if the element has an accessible name, the context the element is used in, and even a combination of these things.</li>
<li>Custom elements can have implicit ARIA semantics too.</li>
<li>Donāt take anyoneās word for it: test it yourself.</li>
</ol>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://html.spec.whatwg.org/multipage/">The HTML Standard</a></li>
<li><a href="https://w3c.github.io/aria/">Accessible Rich Internet Applications (WAI-ARIA)</a></li>
<li><a href="https://w3c.github.io/html-aria/">ARIA in HTML</a></li>
<li><a href="https://w3c.github.io/html-aam/">HTML Accessibility API Mappings (HTML-AAM)</a></li>
<li><a href="https://tetralogical.github.io/screen-reader-HTML-support/lookup/lookup.html">Screen Reader HTML Support ā Lookup</a></li>
<li><a href="https://tetralogical.com/blog/2021/09/29/browsing-with-a-desktop-screen-reader/">Tetralogicalās āBrowsing with a desktop screen readerā</a>
<ul>
<li>The first in a series about browsing with various kinds of assistive technologies (linked within).</li>
</ul>
</li>
<li><a href="https://www.w3.org/WAI/people-use-web/">How People with Disabilities Use the Web</a></li>
</ul>
Forms are a badly designed part of HTML
2025-12-18T00:00:00Z
https://htmhell.dev/adventcalendar/2025/18/
by Jens Grochtdreis<br><p>Forms were likely one of the reasons why browser vendors joined forces in the WHATWG in 2004. They felt that HTML standardization was heading into the wrong direction and wanted more practical relevance. While this may be an oversimplification, if true, it highlights the failure of the WHATWG (i.e., the browser vendors) and, subsequently, the W3C. Although the newly standardized form elements and features all point into the right direction, they are incomplete and unfinished. The fact that this is still the case, even more than ten years after HTML5 became a recommendation, is alarming. I will concentrate in this article on forms and especially on missing elements, inconsistent behaviour and the problems with styling.</p>
<h2 id="new-form-elements">New Form Elements</h2>
<p>The WHATWG introduced several new form elements and attributes into the HTML specification, e.g. the email input, telephone input, date field, and range slider. Most of these are simply new types of the input element. This approach was clever ā a great example of progressive enhancement. If a browser hasnāt implemented a particular input type yet, a plain text field serves as a fallback. Developers can then use JavaScript if needed. However, when the new elements are supported, JavaScript becomes unnecessary.</p>
<p>This progressive-enhancement model works well for simple inputs. So these new elements are intentionally very simple ā essentially shortcuts for single-value fields. They combine regular expressions for validation, an ARIA role, and sometimes trigger a specialized virtual keyboard on mobile phones and tablets. They are similar to web components based on the input element, but are integrated directly into the browser, so no JavaScript is needed for the element to appear. And because they are standardized, the code is consistent across implementations.</p>
<p>Combinations of different form elements have unfortunately not been considered at all. As examples, I would like to mention the <a href="https://open-ui.org/components/combobox.explainer/">combobox</a> and the <a href="https://open-ui.org/components/enhanced-range-input.explainer/">range slider</a>.</p>
<h3 id="the-combobox">The Combobox</h3>
<p>The <a href="https://component.gallery/components/combobox/">combobox</a> is a combination of a select and a search field. Fluent UI provides a <a href="https://fluentsite.z22.web.core.windows.net/0.66.2/maximize/dropdown-example-search-multiple-shorthand/false">combobox component</a>, as does <a href="https://ant.design/~demos/select-demo-multiple">Ant Design</a>. In both cases, only the search field is a form field. The select is simulated by a list in Fluent, while Ant Design uses a div. The selected options are displayed as (fake) buttons using span and svg elements. This has little to do with actual forms.</p>
<figure>
<img src="https://htmhell.dev/adventcalendar/2025/18/combobox-github.png" alt="A button has opend ea layer which is in essence a combination of an input-field with search-icon and a select element." loading="lazy" width="306" height="372" />
<figcaption>The combobox (called select-panel) from the <a href="https://primer.style/product/components/select-panel/">Github-design system</a>.</figcaption>
</figure>
<figure>
<img src="https://htmhell.dev/adventcalendar/2025/18/combobox-ant-design.png" alt="Combobox showing a select with two marked entries. These entries are as well represented as clickable tags inside a formfield where you could type for new tags/entries." loading="lazy" width="407" height="385" />
<figcaption>The combobox from the design-system of <a href="https://ant.design/~demos/select-demo-multiple">ant-design</a>.</figcaption>
</figure>
<h3 id="the-enhanced-range-element">The Enhanced Range Element</h3>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/range">range element</a> can unfortunately only represent a single value. The frequently needed <a href="https://open-ui.org/components/slider.research/">range slider</a> for two or more values (e.g. for shopping portals) does not exist. You'll have to build it yourself. The well-known <a href="https://refreshless.com/nouislider/">noUiSlider</a> simulates the slider for one or more values consistently with a div. At least ARIA attributes are used throughout. The same goes with the version by <a href="https://quasar.dev/vue-components/range/">Quasar</a>. No form elements were harmed. Itās a pity that this obvious use case was not standardized.</p>
<h2 id="built-in-validation">Built-in Validation</h2>
<p>Standardizing validation in the browser was a smart idea. There are built-in regex rules for validation, which can be manually overridden. Validation is controlled via attributes and a standardized JavaScript API, which is great. In practice, however, you quickly run into details that arenāt flexibly solved. This leads to custom solutions using JavaScript.</p>
<p>There is no simple, built-in way to collect all error messages and display them at the top or bottom of the form without JavaScript. Nor is there a way to communicate the total number of errors to the user. Error messages at the element itself are shown via a popup, which cannot be styled or repositioned. For any improvements in usability and clearer communication, you'll need JavaScript. While this is done through a unified API, it still isnāt possible with plain HTML.</p>
<p>HTML needs better built-in options for handling errors. We should be able to control this with attributes, not JavaScript. The less JavaScript is needed, the faster the page will be.</p>
<h2 id="inconsistent-browser-support">Inconsistent browser support</h2>
<p>Form elements don't have standardized, consistent capabilities or appearance. I don't know whether browser vendors weren't willing to align on this or simply didn't see the need. Either way, consistent usability, features, and appearance would help both users and developers. For example, browsers hook into the operating system's built-in color picker, which seems sensible ā but these pickers canāt be styled and differ in functionality. Android and iOS replace the HTML date picker with their own native widget, which is great on mobile. But on desktop, each browser presents a completely different UI, and none of it is customizable.</p>
<p>The number input offers increment/decrement arrows in one browser but not in another. The usefulness of these arrows is debatable. The date field is only read aloud <a href="https://tetralogical.github.io/screen-reader-HTML-support/lookup/lookup.html#input-date">by the native screen reader on iOS</a>) when a date is already present; it is not announced as a date field. The email input is announced as an email field <a href="https://tetralogical.github.io/screen-reader-HTML-support/lookup/lookup.html#input-email">only by Voice Over on MacOS</a>. Other screen readers don't announce the semantics. If browser vendors are leading the standardization of these features, I expect better coordination and consistent implementation.</p>
<p>Semantic information needs to be exposed to screen readers. Without that, the new input type feels only half-baked ā basically just a text input with built-in validation and a tailored onscreen keyboard. And both of those could have been achieved without introducing a dedicated email input.</p>
<p>Inconsistent behaviour and appearance may or may not irritate users. It mostly irritates developers, designers and their clients. Inconsistency is a nightmare if you want to provide a seamless and identical look and feel of your page/application through the differnet devices and browsers. It might as well irritate users if the form looks very different on a smartphone and a notebook.</p>
<h2 id="partially-miserable-styling-options">Partially Miserable Styling Options</h2>
<p>Styling form elements is a major challenge that often ends in failure. Developers frequently hide the native control and style the label instead ā or hide the control entirely and replace it with a JavaScript-driven construction of divs or lists that can be freely styled.</p>
<p>Fundamentally, styling form elements is miserable. This is why <a href="https://2024.stateofhtml.com/en-US/features/forms/">58% of participants</a> in the "State of HTML 2024" survey identified styling as by far the biggest pain point with forms.</p>
<p>Browser vendors are currently more active in CSS than in HTML, so thereās a faint glimmer of hope on the horizon. Chromium has introduced the customizable select, based on an <a href="https://open-ui.org/components/customizableselect/">Open UI proposal</a>, and Brecht deRuyte has dedicated a <a href="https://utilitybend.com/blog/the-customizable-select-part-one-history-trickery-and-styling-the-select-with-css">series of articles</a> on the new, fantastic possibilities.</p>
<p>Itās a start, but the broader problem remains: all controls hidden inside the browserās shadow DOM need consistent, standardized structure so they can be styled reliably. At CSSDay 2025, Tim Nguyen presented the Working Draft ā<a href="https://www.w3.org/TR/2025/WD-css-forms-1-20250325/">CSS Form Control Styling Level 1</a>ā ā early work, but promising. I hope it matures quickly and finds its way into browsers.</p>
<p>Without meaningful progress here, weāll be stuck with <a href="https://open-ui.org/components/datepicker.research/">JavaScript-based date pickers</a> and other custom controls for the foreseeable future. Whatās the point of having a native element if we have to replace it at the first opportunity because we canāt style it?</p>
<p><a href="https://wpt.fyi/interop-2025">Interop 2025</a> didnāt include any focus on form controls. Hopefully some proposals make it into Interop 2026 once the <a href="https://github.com/web-platform-tests/interop/blob/main/2026/selection-process.md">selection process</a> concludes (hopefully by the time this article is published).</p>
<h2 id="lack-of-further-development">Lack of Further Development</h2>
<p>We are still missing important form elements, and developers have to simulate them with JavaScript. Standardizers could have addressed these gaps in recent years. They didn't. Could it be that they think that we already have all the "building blocks" needed ā that developers can simply assemble new controls using JavaScript and Web Components?</p>
<p>Taken to its logical extreme, this argument would reduce HTML to a handful of primitive elements. Everything else would be rebuilt as custom components, each with its own attributes, ARIA wiring, and implementation quirks. That canāt seriously be the goal.</p>
<p>In my view, HTML elements are, in a sense, browser-specific web components, but with some crucial advantages: they do not require JavaScript, and they behave the same for all end users and devices. Non-standard controls, on the other hand, come in dozens of variations, built on different foundations, and with varying quality. Standardization exists precisely so we donāt all have to reinvent them.</p>
<h2 id="hope-is-rising">Hope Is Rising</h2>
<p>The lack of standardization leads to many different approaches to the same problem. What they have in common is that they have little or nothing to do with actual form elements.</p>
<p>Fortunately, practitioners have come together in the "<a href="https://open-ui.org/">Open UI</a>" community group to advance the standardization of HTML and CSS. They describe several features that are missing as standards in HTML. For the combobox, Open UI has created <a href="https://open-ui.org/components/combobox.explainer/">a proposal</a>. I hope it will be implemented in browsers and standardized soon.</p>
<p>The same applies to the range slider with more than one value. Open UI also offers a great idea for a long-overdue <a href="https://open-ui.org/components/enhanced-range-input.explainer/">extension of the standard</a>.</p>
<h2 id="a-new-wave-of-innovation-is-needed">A New Wave of Innovation Is Needed</h2>
<p>This article is a very rough overview of the state of forms. My main demands for innovation are:</p>
<ol>
<li>We need more complex form fields as standard elements, such as a combobox.</li>
<li>We need a range input with multiple handles.</li>
<li>We need much better styling opportunities for every form element. The new stylable select shows the right direction.</li>
<li>Form validation should be controlled by HTML-attributes instead of JavaScript. It's styling should be easy and consistent between browsers.</li>
<li>Screenreaders should communicate form semantic without flaws.</li>
</ol>
<p>The innovation of forms should be part of a much larger initiative. WHATWG and W3C demonstrated at the beginning of the millennium that this is possible. After two decades, it is time to revive this spirit and take HTML to a new level. Weāve seen hints of this recently ā elements like <code><dialog></code>, the popover API, and renewed work on customizable form controls show that the platform can still evolve in meaningful ways. But these feel like isolated wins rather than part of a broader push.</p>
<p>Modern websites no longer fit the document-centric model HTML was created for. A typical news homepage mixes headlines, images, teasers, and interactive elements in ways the original spec never anticipated. The New York Times even present teasers without headlines at all. This diversity shows how little shared foundation there is for developers today ā and why HTML needs a broader, more coordinated evolution beyond isolated improvements.</p>
<p>Modern Websites and especially Webapps need a new paradigm. They evolved far beyond the inventor's idea and it won't stop evolving. The W3C should respond with new elements and paradigms. The document analogy should stand alongside interactive applications as equals. The more that is standardized in this regard, the better it is for the industry. And end users will also benefit from consistently high-quality websites.</p>
Don't leave the screen reader hungry
2025-12-17T00:00:00Z
https://htmhell.dev/adventcalendar/2025/17/
by Geri Reid<br><p>Screen readers donāt always announce whatās visually on screen. This article explores that gap - through the medium of burritos.</p>
<h2 id="the-burrito-you-cant-order">The burrito you can't order</h2>
<p>A customer complained they couldn't order a burrito. "The menu advertises burritos, but my screen reader won't let me order one!"</p>
<p>I was baffled. The menu was about as Mexican as a Yorkshire pudding. There was no mention of a burrito.</p>
<p>After a lot of searching, I discovered someone had dropped an emoji into the heading:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Sandwiches šÆ<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span></code></pre>
<p>Visually, it looked decorative and <em>kind of like a sandwich</em>. But screen readers don't read an emoji as decoration, they announce its <a href="https://unicode.org/emoji/charts/full-emoji-list.html">Unicode character name</a>. So screen reader users heard: "Sandwiches, Burrito."</p>
<p>That little emoji isn't just sitting in your markup looking pretty. It's making promises your website can't keep. One tiny flourish in your heading tag and suddenly your sandwich bar sounds like a taqueria.</p>
<h2 id="whats-on-the-menu?">What's on the menu?</h2>
<p>What I'm highlighting here is screen readers don't consistently announce what you see visually on the screen. This article explores the gap between what HTML gives you for free and what you need to supply yourself.</p>
<p>I've made some quick reference tables to demonstrate how popular screen readers JAWS, NVDA, and VoiceOver handle elements, showing where HTML does the work and where you need to add stuff in addition.</p>
<p>Documenting and recording screen reader requirements is also problematic, so it sometimes falls through the gaps between design and code. Iāve got some ideas to help solve this.</p>
<h3 id="you-hungry?">You hungry?</h3>
<p>Since you're probably craving Mexican food by now, we're going to frame this through the medium of burritos. Put your apron on, taquero - it's time to get cooking!</p>
<p>Letās imagine the browser is your tortilla: it holds everything together. HTML adds some fillings by default. For a successful burrito, you need the right toppings. The trick is knowing which toppings to add and when to stop.</p>
<h2 id="what-you-get-for-free-(the-fillings)">What you get for free (the fillings)</h2>
<p>Like a burrito that comes with rice, beans and your choice of protein, semantic HTML automatically packs the essentials into your tortilla. Correctly marked-up headings, links, buttons, lists, form controls, and tables are announced by default. And like a burrito starts with a good tortilla, screen readers work best when paired with their preferred browser:</p>
<ul>
<li>JAWS with Chrome on Windows</li>
<li>NVDA with Firefox or Chrome on Windows</li>
<li>VoiceOver with Safari on Apple</li>
</ul>
<p>Here's how JAWS, NVDA and VoiceOver screen readers announce for me using their default settings (I've included my testing specs in the footer). Use this as a guide. Your announcements might differ slightly based on verbosity and device settings, browser and software version. The way you navigate also changes what you hear. For example, using a heading shortcut key may produce a different announcement than letting the screen reader read through linearly.</p>
<p>The exact announcement doesn't really matter.</p>
<p>What matters is knowing what you get for free and when you have to add something to get meaningful announcements.</p>
<div class="u-oa" tabindex="0" role="region" aria-label="Exemplary announcements">
<table>
<thead>
<tr>
<th>Element</th>
<th>JAWS says</th>
<th>NVDA says</th>
<th>Mac OS VoiceOver says</th>
<th>Annotation</th>
</tr>
</thead>
<tbody>
<tr>
<td><code><h1>Burritos</h1></code></td>
<td>āBurritos, Heading level 1ā</td>
<td>āBurritos, Heading level 1ā</td>
<td>āHeading level 1, Burritosā</td>
<td>No extras needed. Use correct heading levels for structure.</td>
</tr>
<tr>
<td><code><button>Checkout</button></code></td>
<td>āCheckout buttonā</td>
<td>āCheckout, buttonā</td>
<td>āCheckout, buttonā</td>
<td>No extras needed.</td>
</tr>
<tr>
<td><code><a href="burrito.html">Order burrito</a></code></td>
<td>āOrder burrito, linkā</td>
<td>āOrder burrito, linkā</td>
<td>ālink, Order burritoā</td>
<td>No extras needed. Just make sure the link text makes sense out of context.</td>
</tr>
<tr>
<td><code><ul> <li>Burrito</li> <li>Taco</li> </ul></code></td>
<td>āList of 2 items. Bullet Burrito. Bullet Taco. List endā</td>
<td>āList with 2 items. Bullet Burrito. Bullet Taco. Out of listā</td>
<td>āList 2 items.
Bullet Burrito, 1 of 2.
Bullet Taco, 2 of 2.
End of list.ā</td>
<td>No extra needed.</td>
</tr>
<tr>
<td><code><label for=name">Name</label> <input id="name" type="text"></code></td>
<td>āName, editā</td>
<td>āName, editā</td>
<td>āName, edit textā</td>
<td>No extra needed.</td>
</tr>
<tr>
<td><code><input id="cheese" type="checkbox"> <label for="cheese">Extra cheese</label></code></td>
<td>āExtra cheese, check box, not checkedā</td>
<td>āCheck box, not checked, Extra cheeseā</td>
<td>āExtra cheese, unticked, tick boxā</td>
<td>No extra needed.</td>
</tr>
<tr>
<td><code><button disabled>Not taking orders</button></code></td>
<td>āOrder, button, unavailableā</td>
<td>āButton unavailable, Orderā</td>
<td>āOrder, dimmed, buttonā</td>
<td>No extra needed. Visually appears disabled, not focusable.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="what-you-must-supply-(the-toppings)">What you must supply (the toppings)</h2>
<p>Hereās where you roll up your sleeves. These elements stay silent or may not announce as youād expect until you add the right toppings.</p>
<div class="u-oa" tabindex="0" role="region" aria-label="Exemplary announcements with missings attributes">
<table>
<thead>
<tr>
<th>Element</th>
<th>JAWS says</th>
<th>NVDA says</th>
<th>Mac OS VoiceOver says</th>
<th>Annotation</th>
</tr>
</thead>
<tbody>
<tr>
<td><code><img src="burrito.png"></code></td>
<td>"Unlabelled graphic"</td>
<td>"Unlabelled graphic"</td>
<td>āUnlabelled imageā or "burrito.png"</td>
<td>Add alt text describing the image. Or use <code>alt=""</code> if decorative.</td>
</tr>
<tr>
<td><code><button><svg>burrito svg icon</svg></button></code></td>
<td>āunlabelled buttonā</td>
<td>ābuttonā</td>
<td>ābuttonā</td>
<td>Add accessible name via <code>aria-label</code> or <code>aria-labelledby</code>.</td>
</tr>
<tr>
<td><code><iframe src="#"></iframe></code></td>
<td>āframeā, or āframe 2 of 4ā if multiple</td>
<td>āframeā, or āframe 2 of 4ā if multiple</td>
<td>āframeā</td>
<td>Add a descriptive <code>title</code> for context.</td>
</tr>
</tbody>
</table>
</div>
<p>The trick: add the toppings that matter, but don't overload the burrito. An overstuffed burrito can fall into your lap, potentially ruining a first date.</p>
<h2 id="edge-cases-and-oddities-(htmhells-kitchen)">Edge cases and oddities (HTMHell's Kitchen)</h2>
<p>These ingredients look tasty but behave unpredictably under heat. Use with caution or your markup might fall apart.</p>
<div class="u-oa" tabindex="0" role="region" aria-label="Exemplary oddities">
<table>
<thead>
<tr>
<th>Element</th>
<th>JAWS says</th>
<th>NVDA says</th>
<th>Mac OS VoiceOver says</th>
<th>Annotation</th>
</tr>
</thead>
<tbody>
<tr>
<td><code><strong>Burrito!</strong></code></td>
<td>"Burrito"</td>
<td>"Burrito"</td>
<td>"Burrito"</td>
<td>Emphasis not reliably spoken on default verbosity settings. Don't rely on for meaning.</td>
</tr>
<tr>
<td><code><em>Spicy</em></code></td>
<td>"Spicy"</td>
<td>"Spicy"</td>
<td>"Spicy"</td>
<td>Emphasis not reliably spoken on default verbosity settings. Don't rely on for meaning.</td>
</tr>
<tr>
<td><code>Ā£8 <s>Ā£10</s></code></td>
<td>"£8 strikethough deletion £10"</td>
<td>"£8 deleted £10"</td>
<td>"£8 £10" or "£8 deletion £10"</td>
<td>Strikeout not reliably announced.</td>
</tr>
<tr>
<td><code><sup>2</sup> / <sub>2</sub></code></td>
<td>"2, 2"</td>
<td>"2, 2"</td>
<td>"2, 2"</td>
<td>Not read as superscript or subscript.</td>
</tr>
<tr>
<td><code><hr></code></td>
<td>"separator"</td>
<td>"separator"</td>
<td>Silent</td>
<td>Don't rely on for meaning. Add <code>aria-label</code> if the separation needs context.</td>
</tr>
<tr>
<td><code><abbr title="Street Provisions In Corn-based Envelopes">SPICE</abbr></code></td>
<td>"SPICE, Street Provisions In Corn-based Envelopes"</td>
<td>"Spice"</td>
<td>"Spice"</td>
<td>Full title not reliably announced. Spell out abbreviation in text.</td>
</tr>
<tr>
<td>šÆ</td>
<td>"Burrito"</td>
<td>"Burrito"</td>
<td>"Burrito"</td>
<td>Emojis announce their <a href="https://unicode.org/emoji/charts/full-emoji-list.html">Unicode name</a>. For decorative use, add <code>aria-hidden</code>.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="help!-my-burrito-is-busted!">Help! My burrito is busted!</h2>
<ul>
<li>
<p>If you're second-guessing what should announce, TetraLogical recently released a <a href="https://github.com/TetraLogical/screen-reader-HTML-support?tab=readme-ov-file">detailed guide to screen reader announcements</a> along with an <a href="https://stevefaulkner.github.io/AT-browser-tests/">HTML Element test file</a> you can run your screen reader over to test elements in isolation.</p>
</li>
<li>
<p>If youāre looking for video examples of how elements and components announce, check out the <a href="https://www.atomica11y.com/accessible-web/">HTML section of atomica11y</a>.</p>
</li>
<li>
<p>If you work on a Mac and need to test on Windows screen readers, try <a href="https://assistivlabs.com/">AssistivLabs</a> or set up an <a href="https://getutm.app/">emulator like UTM</a>. You can run NVDA for free and JAWS has a free 40 minute developer mode which is sufficient for testing. <a href="https://webaim.org/projects/screenreadersurvey10/#primary">40% of the screen reader market uses JAWS</a> so if you only test on VoiceOver you might miss issues.</p>
</li>
</ul>
<h2 id="documenting-screen-reader-requirements">Documenting screen reader requirements</h2>
<p>If you're building a website, you need a consistent way to document screen reader announcements. Don't leave it up to chance. Current design tools don't have space for accessibility information and much of it is non-visual, so it easily gets overlooked during implementation.</p>
<h3 id="documenting-with-design-annotation-kits">Documenting with design annotation kits</h3>
<p>Visual annotation kits for Figma, Sketch or Penpot offer a solution. There are plenty of excellent open-source examples shared in community files that you can tailor to your organisation's needs. If you're looking for inspiration, Jan Maarten and Daniel Henderson-Ede did a talk showcasing how <a href="https://www.youtube.com/live/O1GmngpGokU?si=Y6uxWmX1Z6pxfRDD">different design teams annotate for accessibility</a> at this year's Inclusive Design 24.</p>
<p>As a designer, I find it helpful to pair with an engineer and mark up design files together to capture all the accessibility requirements.</p>
<p>A problem with sticky note annotations is that they often die in design files once development begins. Developers don't revisit design files, designers move on and the accessibility details you've spent time documenting get lost. Even if they are used effectively in implementation, you end up rewriting a similar set of notes for subsequent projects.</p>
<h3 id="documenting-with-text">Documenting with text</h3>
<p>Text-based documentation in machine-readable formats (YAML, JSON, markdown tables, or structured specifications) solves this. Instead of static notes trapped in design files, if you record your annotations as text you can create queryable records that travel. Think of it like your restaurant having a digital ordering system instead of handwritten tickets. The kitchen gets the exact burrito order every time, with all the special instructions intact.</p>
<p>These formats also unlock AI integration through tools like RAG or MCP servers. When a developer asks an AI assistant about a component, it can surface the exact accessibility requirements. You could even structure these specs to generate automated test criteria.</p>
<p>The long game: shift to living documentation that can be queried on demand. Some clever folks like Nathan Curtis have already started āto explore ways to <a href="https://medium.com/@nathanacurtis/components-as-data-2be178777f21">record specs with data</a>.</p>
<h2 id="lets-wrap-up!-šÆ">Let's wrap up! šÆ</h2>
<ul>
<li>
<p>HTML gives you a tortilla full of free fillings: headings, lists, buttons, form controls, tables. However, some elements require you to add a topping to make them meaningful.</p>
</li>
<li>
<p>Document which toppings are needed so they don't get lost between design and development. Without clear documentation, developers have to guess at the requirements or skip them entirely, leaving accessibility as an afterthought rather than an integral part of the build.</p>
</li>
</ul>
<p>Remember that a good burrito, like good markup, is about knowing which toppings to add and when to stop. Get it right and your screen reader users get a memorable burrito. Get it wrong and they're left with a stain on their shirt.</p>
<h3 id="about-geri-reid">About Geri Reid</h3>
<p>Geri is an accessibility and design systems nerd from London. As design and accessibility lead on design systems at News UK and Lloyds Banking Group, she helped some of the UKās largest media and banking brands to design at scale. She is currently Lead Accessibility Specialist at Just Eat Takeaway.</p>
<ul>
<li>Blog: <a href="https://gerireid.com/">gerireid.com</a></li>
<li>Bluesky: <a href="https://bsky.app/profile/gerireid.com">@gerireid.com</a></li>
<li>LinkedIn: <a href="https://www.linkedin.com/in/gerireid/">@gerireid</a></li>
</ul>
<h4>Screen reader testing on:</h4>
<ul>
<li>JAWS (2025) with Chrome Version 140.0.7339.128 (Official Build) (64-bit) on Windows 11</li>
<li>NVDA (2025) with Chrome Version 140.0.7339.128 (Official Build) (64-bit) on Windows 11</li>
<li>VoiceOver (2025) with Safari Version 26.0.1 on Mac, Sequoia 15.7.1</li>
</ul>
Giving pages a clear shape by using headings
2025-12-16T00:00:00Z
https://htmhell.dev/adventcalendar/2025/16/
by Steve Barnett<br><p>We can make our pages easier to understand by using headings to give our pages a clear shape. Our users might visually scan the page, use an extension or bookmarklet to list the headings, navigate using assistive technology like a screen reader, or ask AI for a summary of the page. High quality headings can make things better for everyone.</p>
<p>In my day job as a Digital Accessibility Consultant, there are a couple of ways that I've seen things go a bit... wonky. Let's go through the three most common issues, and how to fix them.</p>
<h2 id="text-should-not-be-a-heading">Text <em>should not</em> be a heading</h2>
<p>Ah, this one's a real classic! When we have some big and bold text, for Design Reasons, we sometimes take a bit of a shortcut and mark it up as a heading. Let's say an <code><h2></code>, because it seems about the right size, or thatās what it says in the design file. But here's the thing: this text doesn't introduce or describe the content that follows. It just "needs" to be big for the look of it.</p>
<p>This is an issue because when things are marked up as headings that are not headings, it makes the page harder to understand. Users of assistive technology like screen readers hear things read as headings of section that are not headings.</p>
<h3 id="an-example">An example</h3>
<p>Let's say we have a page explaining colours, RGB-style. We might have headings marked up as follows.</p>
<ul>
<li><code><h1></code>Colours
<ul>
<li><code><h2></code>Red</li>
<li><code><h2></code>Green
<ul>
<li><code><h3></code>Make it pop!</li>
</ul>
</li>
<li><code><h2></code>Blue</li>
</ul>
</li>
</ul>
<p>In this case "Make it pop!" is just some big text, designed to be eye-catching. It's not the start of a section of content.</p>
<h3 id="how-to-fix-it">How to fix it</h3>
<p>Stop using HTML and start using CSS. Instead of using a heading element, using a <code><p></code> or <code><span></code> or <code><div></code> element and use CSS to make it big and bold.</p>
<h2 id="text-should-be-a-heading">Text <em>should</em> be a heading</h2>
<p>Now let's come from the other side. We look at a design and see some bold text. Some big, some bigger, some biggest. Sweet! We fling down a bunch of <code><div></code> elements, add some styles and we're done. It looks just like the design, chef's kiss, and so on. But here's the thing: this text looks like a heading, but doesn't have any semantics.</p>
<p>This is an issue because when text is marked up as a heading even though it isnāt one, it makes the page harder to understand. People using bookmarklets or browser extensions to list headings won't see this text in the list of headings.</p>
<h3 id="an-example-2">An example</h3>
<p>Let's say we have a page explaining what the web is made of. We might have some big bold text marked up as follows.</p>
<ul>
<li><code><h1></code>The world wide web
<ul>
<li><code><h2></code>HTML</li>
<li><code><p></code>CSS</li>
<li><code><h2></code>JavaScript</li>
</ul>
</li>
</ul>
<p>In this case "CSS" isn't just a paragraph. It's the start of a section of content.</p>
<h3 id="how-to-fix-it-2">How to fix it</h3>
<p>Stop using CSS and start using HTML. Instead of using a <code><p></code> or <code><span></code> or <code><div></code> element, use a heading element at the right level to give it semantic structure: from <code><h1></code> to <code><h6></code>. If we have some big and bold text that introduces or describes the content that follows, it should probably be a heading.</p>
<h2 id="headings-do-not-reflect-the-content-structure">Headings do not reflect the content structure</h2>
<p>Okay, we've sorted out text that should and shouldn't be a heading: only things that are structural headings are marked as headings. Hooray! There's one more snag that we might hit: when the headings are in a weird order. For example: let's say we have a page listing edible things. We mark up <code><h3></code>Fruit<code></h3></code> as a section, and then <code><h2></code>Apples<code></h2></code> as a sub-section of Fruit. Maybe we've done this because that's what the styles in the design file suggest. But here's the thing: it's wonky because the headings don't represent the hierarchical relationships.</p>
<p>This is an issue because users of assistive technology like screen readers use headings to understand how each section of the page relates to each other and the page as a whole. When the headings are wonky, the shape of the page is harder to understand.</p>
<h3 id="how-to-fix-it-3">How to fix it</h3>
<p>Use HTML to give the headings the correct nesting and ordering. Use CSS to make them look appropriately sized and shiny.</p>
<p>I like to start from the page as a whole and work my way down.</p>
<ol>
<li>Whatās the topic or purpose of this page? That text should be in an <code><h1></code> element near the top of the page.</li>
<li>What are the sections of the page? The name of each section should be in an <code><h2></code> element, at the start of the section.</li>
<li>What (if any) are the subsections of each section? The name of each subsection should be in an <code><h3></code> element, at the start of the subsection.</li>
<li>What (if any) are the sub-subsections of each subsection? The name of each sub-subsection should be in an <code><h4></code> element, at the start of the sub-subsection.</li>
</ol>
<p>And we keep going, down to an <code><h6></code> element. Although if you've reached an <code><h6></code> element, it might be worth reviewing the content and seeing if there's Too Much Stuff there!</p>
<p>The list of headings should read a bit like a table of contents for the page.</p>
<h2 id="other-weird-heading-things">Other weird heading things</h2>
<p>There are other aspects of wonkiness that may occur. Keep a watch for these too!</p>
<ul>
<li><strong>Heading text that doesn't describe the content that follows.</strong> The words of the heading must introduce the section. Ask your friendly Content writer for help!</li>
<li><strong><code><h1></code> shenanigans: no <code><h1></code> element, or multiple <code><h1></code> elements.</strong> Just one <code><h1></code> element, please! It should describe the topic or purpose of page.</li>
<li><strong>Skipped heading levels</strong>, for example: jumping from an <code><h2></code> element to an <code><h4></code> element, without an <code><h3></code> between them. Keep the nesting and order correct: <code><h3></code> elements for subsections of a section with an <code><h2></code> heading.</li>
</ul>
<h2 id="accessibility-nerd-corner">Accessibility nerd corner</h2>
<p>The big three issues we started with all fall under <a href="https://www.w3.org/TR/WCAG22/#info-and-relationships">Web Content Accessibility Guidelines Success Criteria 1.3.1 Info and Relationships (A)</a>: "Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text."</p>
<ul>
<li>The "Text should not be a heading" and "Text should be a heading" issues are about the information.</li>
<li>The "Headings do not reflect the content structure" issue is about structure and relationships.</li>
</ul>
<p>When we spot these issue in the course of an <a href="https://intopia.digital/services/accessibility-usability-testing/">Accessibility Assessment</a>, we usually log them as Medium Severity: it causes problems or frustrations for users.</p>
<p>Headings that aren't descriptive fall under <a href="https://www.w3.org/TR/WCAG22/#headings-and-labels">WCAG Success Criteria 2.4.6 Headings and Labels (AA)</a>. These are usually Medium Severity too.</p>
<h2 id="use-your-head(ings)">Use your head(ings)</h2>
<p>Using headings to give our pages a clear shape makes them easier to understand.</p>
<p>Make sure that:</p>
<ul>
<li>text that functions as a heading is marked up as a heading</li>
<li>text that does not function as a heading is not marked up as a heading</li>
<li>headings reflect the content structure</li>
</ul>
<h3 id="useful-tools">Useful tools</h3>
<p>Two of my favourite ways to visualise headings are:</p>
<ul>
<li>the Headings bookmarklet at <a href="https://accessibility-bookmarklets.org/install.html">Accessibility Bookmarklets</a></li>
<li>the Headings toggle (in Ad hoc tools) of the <a href="https://accessibilityinsights.io/docs/web/overview/">Accessibility Insights for Web</a> extension.</li>
</ul>
<p>Both of them add annotation-like boxes and text, making it easier scroll through and visually spot weird heading things.</p>
Themās the Breaks
2025-12-15T00:00:00Z
https://htmhell.dev/adventcalendar/2025/15/
by Tyler Sticka<br><p>On the web, itās easy to take line breaks for granted.</p>
<p>We get them for free between our headings, paragraphs, list items, <code><div></code> elements and more. We display them as-is in our code snippets thanks to <code><pre></code>. And most magically of all, our browsers insert breaks <em>automatically</em> where lines of text (or other text-like āinlineā elements) would otherwise outgrow their container.</p>
<p>But sometimes, that isnāt enough.</p>
<p>Some words are too long and continuous to break automatically. Some words can be āorphanedā onto their own, lonely line. Occasionally, our content demands an overt break; more often, our designs call for their addition or removal.</p>
<p>So it makes sense that HTML provides a few options for managing mid-content breaks. Some famously overused, others less known or understood:</p>
<ul>
<li>The ābreakā element, <code><br></code></li>
<li>The āword break opportunityā element, <code><wbr></code></li>
<li>The āsoft hyphenā character, <code>&shy;</code></li>
<li>The ānon-breaking spaceā character, <code>&nbsp;</code></li>
</ul>
<p>Letās ābreakā down (<a href="https://www.youtube.com/watch?v=AKtwlHV1-O8">nyuk, nyuk</a>) those techniques: What they do, when theyāre appropriate, and alternatives to consider.</p>
<h2 id="the-"break"-element-lessbrgreater">The ābreakā element, <code><br></code></h2>
<p>99% of the time, line breaks in text that are truly <em>meaningful</em> to your content will justify a new paragraph, list item, <code><div></code> or other block element.</p>
<p>The <code><br></code> (ābreakā) element is for those rare exceptions.</p>
<p>For example, a <code><br></code> can force a break between lines of poetry or song (apologies to <a href="https://en.wikipedia.org/wiki/One_Step_Closer_(Linkin_Park_song)">Linkin Park</a>):</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Everything you say to me<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> (Takes me one step closer to the edge)<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> (And Iām about to break)</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>Or within an address:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>address</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Breakside Brewery<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> 1570 NW 22nd Ave.<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Portland, Oregon 97210</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>address</span><span class="token punctuation">></span></span></span></code></pre>
<p>And⦠thatās pretty much it. As commonplace as <code><br></code> is, itās rarely preferable to more semantic HTML.</p>
<h2 id="the-"word-break-opportunity"-element-lesswbrgreater">The āword break opportunityā element, <code><wbr></code></h2>
<p>The <code><wbr></code> element is <code><br></code>ās less famous, more introverted cousin. It inserts a break <em>only</em> when the text will overflow and the browser canāt find a ābreak opportunityā (whitespace) of its own.</p>
<p>You wouldnāt want to use <code><wbr></code> for most text: Thereās no hyphenation or anything to indicate where breaks occur. But it can be useful when a string has predictable breakpoints that arenāt spaces.</p>
<p>For example, the slashes in a URL or directory path:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">https://htmhell.dev<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>wbr</span><span class="token punctuation">></span></span>/adventcalendar<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>wbr</span><span class="token punctuation">></span></span>/2025<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>wbr</span><span class="token punctuation">></span></span>/17<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>wbr</span><span class="token punctuation">></span></span>/index.html</span></code></pre>
<p>Or dot notation in an object chain:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">namespace<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>wbr</span><span class="token punctuation">></span></span>.class<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>wbr</span><span class="token punctuation">></span></span>.object<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>wbr</span><span class="token punctuation">></span></span>.property</span></code></pre>
<p><code><wbr></code> should only be used to signify clear break points: You should <em>not</em> attempt to auto-insert <code><wbr></code> elements willy-nilly as a form of general overflow avoidance. It is also a poor choice for any list-like content, such as breadcrumb navigation.</p>
<h2 id="the-"soft-hyphen"-character-andshy;">The āsoft hyphenā character, <code>&shy;</code></h2>
<p>The <code>&shy;</code>(āsoft hyphenā) character reference (<code>&#173;</code> for the Unicode stans) functions a lot like <code><wbr></code>, except a hyphen is inserted just before the break:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">anti<span class="token entity named-entity" title="­">&shy;</span>dis<span class="token entity named-entity" title="­">&shy;</span>establishmen<span class="token entity named-entity" title="­">&shy;</span>taria<span class="token entity named-entity" title="­">&shy;</span>nism</span></code></pre>
<p><code>&shy;</code> is also surprisingly configurable via CSS. You can replace the hyphens with a character of your choice:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">p</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">hyphenate-character</span><span class="token punctuation">:</span> <span class="token string">"āÆ"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Or disable them entirely:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">p</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">hyphens</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The hyphenation makes <code>&shy;</code> more suitable for typical prose than <code><wbr></code>. That said, hyphenation in general is a bit of a minefield:</p>
<ul>
<li>It doesnāt work well in every language.</li>
<li>Breaking up a word between two lines, hyphenated or not, can be challenging for many readers.</li>
<li>While hyphenation has a long and rich typographic history, its readability has always been highly dependent on the size, layout and justification of the overall text. Dynamic content and responsive containers make it that much tougher to get right.</li>
</ul>
<p>I occasionally find <code>&shy;</code> helpful when Iām writing and notice a word flowing in a particularly troublesome way. Iād consider frequent usage a signal to simplify my verbiage or tweak my design.</p>
<h2 id="the-"non-breaking-space"-character-andnbsp;">The ānon-breaking spaceā character, <code>&nbsp;</code></h2>
<p>Normally, whitespace characters are the most reliable indication of a line break opportunity. The <code>&nbsp;</code> character openly <em>defies</em> that convention, applying a space that is, much like <a href="https://en.wikipedia.org/wiki/Unbreakable_Kimmy_Schmidt">Kimmy Schmidt</a>, <em>unbreakable</em>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">Keep<span class="token entity named-entity" title=" ">&nbsp;</span>it<span class="token entity named-entity" title=" ">&nbsp;</span>together</span></code></pre>
<p>(The non-breaking space is just one of <a href="https://en.wikipedia.org/wiki/Whitespace_character#Unicode">many whitespace characters</a> that will prevent a string from breaking as youād normally expect.)</p>
<p>As useful as that sounds, <code>&nbsp;</code> and its cousins should be considered a last resort. They donāt play very well with other techniques for managing breaking and text flow, and theyāre virtually impossible to style without additional selectors or <a href="https://css-tricks.com/modifying-specific-letters-with-css-and-javascript/">truly epic hacks</a>.</p>
<h2 id="gently-apply-your-breaks">Gently Apply Your Breaks</h2>
<p>If youāve followed along to this point, you may notice a pattern: These techniques all have pretty limited use cases!</p>
<ul>
<li><code><br></code> for the rare break thatās actually part of the content (poems, addresses, etc.)</li>
<li><code><wbr></code> for weird run-on strings</li>
<li><code>&shy;</code> for very occasional hyphenation in prose</li>
<li><code>&nbsp;</code> when you must avoid a break at all cost</li>
</ul>
<p>For other mid-content break scenarios, your best bet is CSS!</p>
<p>With <code>display</code>, we can stack inline elements as if they were blocks:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Ms. Boop Squanklin,<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Beloved Activist <span class="token entity named-entity" title="&">&amp;</span> Icon<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>Or flow block elements together:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>hgroup</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span> <span class="token property">column-gap</span><span class="token punctuation">:</span> 1ch<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Subtitle<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>hgroup</span><span class="token punctuation">></span></span></span></code></pre>
<p>Or keep key phrases wrapping as one:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> āCome Togetherā by</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> The Beatles</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>We can encourage long strings to wrap more aggressively with <code>overflow-wrap</code>:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">p</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">overflow-wrap</span><span class="token punctuation">:</span> anywhere<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* or */</span></span><br /><span class="highlight-line"> <span class="token property">overflow-wrap</span><span class="token punctuation">:</span> break-word<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* or */</span></span><br /><span class="highlight-line"> <span class="token property">word-break</span><span class="token punctuation">:</span> break-all<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Rescue typographic orphans with <code>text-wrap</code>:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">p</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">text-wrap</span><span class="token punctuation">:</span> balance<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* or */</span></span><br /><span class="highlight-line"> <span class="token property">text-wrap</span><span class="token punctuation">:</span> pretty<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Or micro-manage break behavior with <code>white-space</code>:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.yolo-single-line</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>(We can even apply hyphenation and truncation via CSS, but these present their own challenges. See <a href="https://cloudfour.com/thinks/justified-text-better-than-expected/">my justified text explorations</a> and <a href="https://css-tricks.com/embracing-asymmetrical-design/">some classic truncation wisdom from Karen McGrane</a>.)</p>
<p>Content that calls for a semantic break is rare, but real: Itās good to understand your HTML options for that scenario. But once youāve plopped in <em>one</em> quick <code><br></code>, its immediacy makes it tempting to overuse.</p>
<p>Resist the urge, write good markup, and embrace the accessibility, power and maintainability of CSS alternatives. Your audience and your projectās future maintainers will be happy you did!</p>
The Wonderful World of Web Feeds
2025-12-14T00:00:00Z
https://htmhell.dev/adventcalendar/2025/14/
by Maureen Holland<br><p>Web feeds are incredible! And a bit confusing! Why are the feed links often called āRSSā? And why is this āRSSā feed in an <code>atom.xml</code> file⦠hang on, what is <code>feed.json</code> for? What are they even feeding into anyway?</p>
<p>To start, web feeds are often referred to as āRSSā because RSS is the oldest format. <a href="https://www.rssboard.org/rss-specification">RSS</a> stands for Really Simple Syndication. It is an XML-based specification for web content syndication (<a href="https://help.apple.com/itc/podcasts_connect/#/itcb54353390">including podcasts</a>).</p>
<p>Syndication is the sale or licensing of material for publication or broadcasting by others. In <a href="https://web.archive.org/web/20091009143514/http://www.museum.tv/archives/etv/S/htmlS/syndication/syndication.htm">broadcast syndication</a>, networks sell reruns of their original shows to other platforms, where those shows might reach a larger audience. In web content syndication, feeds package web content into a format that can ārerunā on a feed reader application. A key difference is that feeds are not sold to feed readers. The āaudienceā for web feeds has a much more active role to play. They are subscribers, choosing what content they want to follow and what reader they want to follow it on.</p>
<p>Other web content syndication specifications include <a href="https://datatracker.ietf.org/doc/html/rfc4287">Atom</a> (also XML-based) and <a href="https://www.jsonfeed.org/version/1.1/">JSON</a>. If youāre interested, <a href="https://css-tricks.com/working-with-web-feeds-its-more-than-rss/#aa-rss-vs-atom-vs-json">CSS Tricks has a breakdown of the technical distinctions between these formats</a>. A web feed (even one that says itās an āRSSā feed) could be any of these formats under the hood.</p>
<p>A very basic web feed looks like this: <a href="https://maureenholland.ca/magpie/feed.xml">https://maureenholland.ca/magpie/feed.xml</a></p>
<p>Simplified example below:</p>
<pre class="language-xml"><code class="language-xml"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rss</span> <span class="token attr-name">version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2.0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>channel</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">atom:</span>link</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>https://maureenholland.ca/magpie/feed.xml<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>self<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>application/rss+xml<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Magpie<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span><span class="token punctuation">></span></span>https://maureenholland.ca/magpie/<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>link</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>description</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Blog of writer and web developer Maureen Holland. Untidy nest of shiny things.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>description</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>language</span><span class="token punctuation">></span></span>en-ca<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>language</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>item</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>A Vanilla Personal Site<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> https://maureenholland.ca/magpie/a-vanilla-personal-site</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>link</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>guid</span> <span class="token attr-name">isPermaLink</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> https://maureenholland.ca/magpie/a-vanilla-personal-site</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>guid</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>pubDate</span><span class="token punctuation">></span></span>Tue, 18 Apr 2023 12:00:00 GMT<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>pubDate</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>description</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> I rebuild my personal site every few years. This time, I decided I wanted to go as minimal as possible. It's been the most enjoyable iteration.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>description</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>item</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>channel</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rss</span><span class="token punctuation">></span></span></span></code></pre>
<p>This XML file includes general info about the feed (<code>channel</code>) and lists one <code>item</code> which contains a <code>title</code>, <code>link</code>, <code>description</code>, publication date (<code>pubDate</code>), and globally unique identifier (<code>guid</code>).</p>
<p>A feed reader, like <a href="https://feedbin.com/home">Feedbin</a>, <a href="https://netnewswire.com/">NetNewsWire</a>, or <a href="https://www.newsblur.com/">NewsBlur</a>, is able to parse that information and serve it in a human-readable format that will look something like this:</p>
<img src="https://htmhell.dev/adventcalendar/2025/14/simple-feed-in-reader.png" width="1140" height="372" loading="lazy" alt="Black text on white background. Title: A Vanilla Personal Site. Description: I rebuild my personal site every few years. This time, I decided I wanted to go as minimal as possible. Itās been the most enjoyable iteration. Gray text: maureenholland.ca, Apr 18 2023." />
<p>It uses the unique identifier to determine if an item in the feed is new. It can do this for any number of feeds, constantly updating a reading list of your favourite web content.</p>
<p>Importantly, feed readers have no proprietary control over your feed list. If you are dissatisfied with your reader, you can export your feeds to an <a href="https://opml.org/spec2.opml">OPML</a> (Outline Processor Markup Language) file and import them to a new reader later.</p>
<h2 id="the-joy-of-autodiscovery">The Joy of Autodiscovery</h2>
<p>Remember all that stuff about RSS and Atom and XML and JSON? Forget it!</p>
<p>A subscriber shouldnāt have to know any of that technical detail. This is where <a href="https://www.rssboard.org/rss-autodiscovery">RSS Autodiscovery</a> comes in.</p>
<p>You can implement autodiscovery with a single line of HTML in the <code>head</code> of your website (and if youāre using a blog platform, chances are itās already there by default):</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<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>application/rss+xml<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Magpie<span class="token punctuation">"</span></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>https://maureenholland.ca/magpie/feed.xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>Now, no one has to hunt for your siteās subscribe link (or āRSSā link or whatever). They can copy/paste the website address into their feed reader and let the application do the work of finding the feeds.</p>
<img src="https://htmhell.dev/adventcalendar/2025/14/rss-autodiscovery.png" width="1060" height="442" loading="lazy" alt="A feed reader search input with the value: 'https://maureenholland.ca/magpie'. The search correctly returns one rss.xml feed: Magpie, Blog of writer and web developer Maureen Holland." />
<p>If you want, you can also include separate links for different categories. <a href="https://codex.wordpress.org/Customizing_Feeds">WordPress</a>, for example, automatically generates feeds for entries and comments. <a href="https://ghost.org/integrations/custom-rss/">Ghost</a> includes a main post index, author archive, and tag archive.</p>
<p>This is not required but can be helpful if your site has a lot of frequently updated content or a wide range of topics. Subscribers may prefer a subset of content (i.e. long form articles or short āToday I Learnedā posts).</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<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>application/rss+xml<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Everything<span class="token punctuation">"</span></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>https://example.com/feed.xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<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>application/rss+xml<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Articles<span class="token punctuation">"</span></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>https://example.com/articles.xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<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>application/rss+xml<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>TIL<span class="token punctuation">"</span></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>https://example.com/til.xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>I started subscribing to web feeds after reading <a href="https://timkadlec.com/remembers/2023-02-23-investing-in-rss/">Tim Kadlecās Investing in RSS</a>. When Iām online, at some point, I will be checking my feed reader for a bit of inspiration. Itās a form of self-care to step out of the daily grind and step into someone elseās brain for a while. As much as Iāve learned from the articles Iāve read, itās the feeling Iāve had reading them that I remember most, that spark of connection or revelation. If Iāve been offline for a while, the unread notifications can pile up, so I also consider it a form of self-care to select āMark all as read.ā</p>
<p>If youāre already a fan of web feeds, check youāve made it easy for others to find your feed with autodiscovery. If youāre new to web feeds, pick a reader and try it out for a month. Then switch to a different one, <em>just because you can</em>.</p>
<p class="highlight">
This post owes a lot to Matt Webbās great work on <a href="https://aboutfeeds.com/">https://aboutfeeds.com/</a>.
</p>
Hell is other people's markup
2025-12-13T00:00:00Z
https://htmhell.dev/adventcalendar/2025/13/
by Ian Lloyd (Lloydi)<br><p><a href="https://www.htmhell.dev/">HTMLHell</a> started as a site that showed some of the finest, and by that I mean most <strong>awful</strong>, examples of crimes against markup the world has to offer (and how these crimes can be put right). Weāve all seen some shit, man. But somewhere along the line, Manuel started <a href="https://www.htmhell.dev/tips/">HTML Heaven</a>, covering decent markup and clever techniques. It's a good mix of dark and light, yin and yang. And what I wanted to cover in my offering to this annual advent calendar sits firmly in the middle. I can't prevent you from witnessing markup that makes you want to gouge your eyes out with rusty soup spoons, but I may have a solution that helps you understand what you can see in the browser a little more easily.</p>
<p>Before I continue, it might be worth explaining a bit about what I do in my day-to-day role to provide context about why this all came about.</p>
<p>I carry out accessibility audits for multiple clients at <a href="https://tetralogical.com/about/">TetraLogical</a> (or <a href="https://tetralogical.com/services/assessments/">assessments</a> as we refer to them internally). When I encounter something that doesn't behave as it should when trying to navigate using a keyboard, or doesn't sound right when using a screen reader, the first thing I need to check is what is the markup (HTML) behind the elements with issues. Typically, that means right-clicking on the part of the screen where the problem exists and looking at the <strong>Elements</strong> tab in the browser's built-in DevTools feature. I'm also likely to need to check the <strong>Accessibility</strong> panel in DevTools to see what that markup exposes to assistive technology users.</p>
<p>Here's a supoer simple example of TetraLogical's website, showing details of the top navigation element:</p>
<p><img src="https://htmhell.dev/adventcalendar/2025/13/DevTools-with-elements-and-accessibility-panels-information-highlighted.png" alt="The Elements panel is showing, as well as the Accessibility panel. The header navigation, implemented as a element shows clean, simple markup and also reveals its navigation role and its accessible name in the Accessibility panel" /></p>
<ul>
<li>What I hope to see whan I check the markup showing in DevTools: semantic markup that provides structure/meaning to what is rendered on the page (as in the example above).</li>
<li>What I increasingly find: non-semantic markup that is often heavily nested, stuffed full of attributes, and which usually requires multiple steps to expand each node to get the full picture.</li>
</ul>
<p>A few years back, I created a tool that was very much borne out of frustration while doing an audit of a very well known web site. Everything that I checked was just an <a href="https://www.tpgi.com/seeing-the-wood-for-the-trees-demystifying-markup-in-2021/">absolute WALL of attribute-laden markup</a>.<br />
<img src="https://htmhell.dev/adventcalendar/2025/13/markup-de-wall-of-tags-and-attributes.png" alt="Example of some markup that is almost impossible to decipher because it is completely overloaded with CSS classes and other attributes" /></p>
<p>The markup might have been structurally fine, but it really took some effort to discern that that was the case. I had to go through various passes to work out what I was actually looking at to be able to make sense of things. The frustration led me to create the <a href="https://a11y-tools.com/markup-de-crapulator/">HTML De-crapulator</a>, a tool that I would use many, many times in audits that I carried out for years after. But ... I still felt it could be more useful.</p>
<p>The HTML De-Crapulator can provide many ways to simplify markup, such as:</p>
<ul>
<li>Removing specific attributes</li>
<li>Abbreviating specific attributes</li>
<li>Removing empty tags</li>
<li>Removing framework-specific comment tags</li>
</ul>
<p><img src="https://htmhell.dev/adventcalendar/2025/13/de-crapulator.png" alt="The HTML De-Crapulator interface, showing the input, some filtering options and the generated output" /></p>
<p>Most of the time, pressing the 'Check (almost) all of the above' button did the bulk of what is needed to strip selected markup to its bare bones. <em>Most of the time</em> ... Inevitably, with each new site I had to check, I'd find a new collection of custom attributes or tagnames that the tool didn't have in its defaults, so I'd have to customise again and again. The tool does take out a lot of the manual work required to clean up the markup, but I was still finding it to not be as quick as it could be.<br />
What do I want? I want to look at how a given part of the page is built, quickly. Yet <em>this</em> still doesn't feel all that speedy to me:</p>
<ol>
<li>Right click on an element on the page</li>
<li>Select <strong>Inspect</strong></li>
<li>Right click on the node revealed in the <strong>Elements</strong> panel in Dev tools</li>
<li>Copy the Outer HTML</li>
<li>Go to the HTML De-Crapulator and paste</li>
<li>Try the <strong>Check (almost) all of the above</strong> button and see what the results are</li>
<li>Get frustrated by the remnants still there that I really don't care about</li>
<li>Refine, refine, refine until I have the cleaned up markup just so</li>
</ol>
<p>I just wanted to get the markup that <strong>matters</strong>, quickly. What do I mean by markup that matters?</p>
<ul>
<li>Anything that exposes the <code>role</code> of an element to assistive technology users</li>
<li>Anything that exposes the state of an element to assistive technology users</li>
<li>Any attribute that may affect the focusability of an element</li>
</ul>
<p>Anything else is just noise. With that in mind, a few months back I came up with the <a href="https://a11y-tools.com/bookmarklets/#one-click-decrapulator">1-Click De-Crapulator</a>.</p>
<p><img src="https://htmhell.dev/adventcalendar/2025/13/one-click-decrapulator.png" alt="Maybe make this one decorative with empty alt?" /></p>
<p>How does it work? You run the script (as a bookmarklet or you can use <a href="https://chromewebstore.google.com/detail/a11y-tools-bookmarklets/fedddpaapeedmkanpenidomfbebacgoa">the version in the Chrome extension</a> if you prefer) and then do the following:</p>
<ol>
<li>Click on the thing you want to get simplified markup for</li>
<li>That's it. There is no step 2</li>
</ol>
<p>OK, so there <em>sort of</em> is a step 2 ... if you need it, and that's to copy the markup that's presented. But essentially, with one click you can see the markup for the selected node in a super-simplified format, ready to copy and paste if you choose to.</p>
<p><img src="https://htmhell.dev/adventcalendar/2025/13/1-selecting-HTML-node.png" alt="With the 1-Click De-Crapulator running, you hover over the part of the page that you want to inspect, and it shows a border around the current node, as well as an information panel that provides info about the current HTML tag" /></p>
<p><img src="https://htmhell.dev/adventcalendar/2025/13/2-decrapulated-markup.png" alt="The tool shows the cleaned up markup in a dialog with buttons that read 'Close', 'Pick again', Flatten' and 'Show Original'" /></p>
<p>At a glance, you can understand the structure of the item that you selected. All classes and trivial attributes are jettisoned. Only those that may have an impact on how the page is exposed to assistive technology users remain (text alternatives, states, <code>ARIA-*</code> attributes, <code>id</code> attributes ... but only where something else is referencing that element and needs it otherwise all the <code>id</code>s are stripped).</p>
<p>Went too far? You can also quickly switch between the original markup with all attributes intact, should you want to make a quick comparison.</p>
<p><img src="https://htmhell.dev/adventcalendar/2025/13/3-original-markup.png" alt="The same dialog but showing the original markup, indented. The 'Show original' button is indicated as pressed with a change of colour and a tick" /></p>
<p>Didn't go far enough? Perhaps you're seeing endless levels of <code><div></code> nesting that really isn't contributing to meaning or structure? You have the option of flattening it. Here's the before version:</p>
<p><img src="https://htmhell.dev/adventcalendar/2025/13/4-flattened-markup-before.png" alt="Example of markup with multiple layers of nested DIV elements" /></p>
<p>And here is the after:</p>
<p><img src="https://htmhell.dev/adventcalendar/2025/13/5-flattened-markup-after.png" alt="The same markup but with all needless nested DIV elements removed, showing the much more simplified structure" /></p>
<p>Of course, you really are messing with the original markup here, but for the noble reasons of making it understandable and simplified. To save you having to explain each and every time that you simplified the markup when writing up an issue, the tool also wraps the output with Markdown block code backticks and an explanatory phrase that should work for almost every scenario: "Simplified HTML (with some attributes/features removed for clarity)".</p>
<p>As with the original full-fat HTML De-Crapulator, this won't address the root of the problem: namely, developers producing shoddy markup. But if you spend much of your day trying to decipher and remediate other people's markup, which can be hell, this tool can save you a lot of fuss and bother in getting to the bottom of the issue.</p>
A11y Considerations in Math on the Web
2025-12-12T00:00:00Z
https://htmhell.dev/adventcalendar/2025/12/
by Manuel SƔnchez<br><p>Maybe it has happened to you that you wanted to write some formulas in HTML to display on a website, and even though there are multiple ways to do it, accessibility is often not considered in the process. How the formula is read by screen readers is crucial to ensure that we don't leave anyone behind. And the main assistive technologies are in different stages, as we will see.</p>
<p>The web is full of many different and interesting approaches for representing formulas. To name a few, we have TeX/LaTeX source rendered in the browser in different ways, like MathJax or KaTeX, we can use Unicode math, Canvas/WebGL or even simple PNG/JPG or SVG pictures. However, using native <a href="https://developer.mozilla.org/en-US/docs/Web/MathML">MathML</a> is usually one of the best options for this task, even if it wasnāt initially designed for the web. Some of its advantages are that it has its own syntax, MathML, which provides various elements that give the correct semantics to the different parts of a formula, it has good screen reader support, works without JavaScript dependencies, and can be used beyond the browser, as in EPUB or braille/math speech tooling.</p>
<p>Let's take the famous Pythagorean Theorem as an example.</p>
<section style="margin-bottom: 2rem" aria-labelledby="section-0-heading">
<h2 id="section-0-heading">Pythagorean Theorem</h2>
<p>The following example is a visual representation of the formula together with the MathML code.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<msup>
<mi>a</mi>
<mn>2</mn>
</msup>
<mo>+</mo>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
<mo>=</mo>
<msup>
<mi>c</mi>
<mn>2</mn>
</msup>
</math>
</section>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>math</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1998/Math/MathML<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>b<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>=<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>c<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>math</span><span class="token punctuation">></span></span></span></code></pre>
<p>Unlike plain HTML with <code>sup</code> or <code>span</code>, which only describe presentation, MathML defines each role explicitly:</p>
<ul>
<li><code>math</code> represents the entire mathematical expression.</li>
<li><code>msup</code> defines a superscript relationship (a base and an exponent).</li>
<li><code>mi</code> is a mathematical identifier, typically a variable such as <strong>a</strong>, <strong>b</strong>, or <strong>c</strong>.</li>
<li><code>mn</code> is a mathematical number, like <strong>2</strong>.</li>
<li><code>mo</code> is a mathematical operator, such as <strong>+</strong> or <strong>=</strong>.</li>
</ul>
<p>Other alternatives, like the ones mentioned above, may offer similar capabilities, but they typically rely on an assistive or hidden MathML layer. In practice, MathML remains the only web-standard markup that expresses mathematical roles natively in the DOM.</p>
<p>With this approach, the accessibility tree shows good semantics and VoiceOver knows well what to do.</p>
<p><img alt="Accessibility tree view of a MathML formula showing nested semantic elements. The tree includes nodes such as MathMLMath, MathMLSup, MathMLIdentifier, MathMLNumber, and MathMLOperator, representing the structure of the equation a² + b² = c²." src="https://htmhell.dev/adventcalendar/2025/12/mathml-a11y-tree.png" /></p>
<p>However, as we will see throughout the article, screen reader support for the <code>math</code> tag varies across assistive technologies. VoiceOver seems to be doing a pretty good job, <a href="https://www.freedomscientific.com/training/teachers/accessing-math-content-with-jaws-and-fusion/">JAWS also makes it easy for both speech and braille</a>, and <a href="https://github.com/nvaccess/nvda/issues/17667">NVDA needs an add-on to make it work called MathCat</a> because if not, the <code>math</code> tag will be ignored. A major pull request (<a href="https://github.com/nvaccess/nvda/pull/18323">#18323</a>) was merged on 17 November 2025 which integrates MathCAT into NVDA core, meaning users wonāt have to find/install a separate add-on to handle math.</p>
<section aria-labelledby="example-formula">
<h3 id="example-formula">How screen readers interpret the formula</h3>
<details>
<summary>NVDA + Firefox (Windows with MathCAT add-on)</summary>
region a squared plus b squared is equal to c squared space
</details>
<details>
<summary>VoiceOver + Safari (Mac)</summary>
a squared + b squared = c squared, with 5 items, maths
</details>
<details style="margin-bottom: 2rem;">
<summary>VoiceOver + Safari (iOS)</summary>
a squared plus b squared equals c squared, Math
</details>
</section>
<video style="margin-bottom: 1rem;" title="How VoiceOver interprets the formula on Safari" controls="" width="904" height="680">
<source src="https://htmhell.dev/adventcalendar/2025/12/pythagorean-theorem.mp4" type="video/mp4" />
</video>
<p>Let's look at a more complicated case. Instead of just displaying the formula, let's see how to actually prove it and how screen readers will announce it.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>math</span> <span class="token attr-name">display</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>block<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>semantics</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtable</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Step one --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mrow</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>b<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mrow</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>=<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>c<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>4<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>ā
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mfrac</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mfrac</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>b<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Step two --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>b<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>b<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>=<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>c<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>b<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Step three --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>b<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>=<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>c<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mtable</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>annotation</span> <span class="token attr-name">encoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>application/x-tex<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> \begin{aligned} (a + b)^2 &= c^2 + 4 \cdot \left( \frac{1}{2} ab \right)</span><br /><span class="highlight-line"> \\ a^2 + 2ab + b^2 &= c^2 + 2ab \\ a^2 + b^2 &= c^2 \end{aligned}</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>annotation</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>semantics</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>math</span><span class="token punctuation">></span></span></span></code></pre>
<math style="margin-bottom: 2rem;" display="block">
<semantics>
<mtable>
<!-- Step one -->
<mtr>
<mtd>
<msup>
<mrow>
<mo>(</mo>
<mi>a</mi>
<mo>+</mo>
<mi>b</mi>
<mo>)</mo>
</mrow>
<mn>2</mn>
</msup>
</mtd>
<mtd>
<mo>=</mo>
</mtd>
<mtd>
<msup>
<mi>c</mi>
<mn>2</mn>
</msup>
<mo>+</mo>
<mn>4</mn>
<mo>ā
</mo>
<mo>(</mo>
<mfrac>
<mn>1</mn>
<mn>2</mn>
</mfrac>
<mi>a</mi>
<mi>b</mi>
<mo>)</mo>
</mtd>
</mtr>
<!-- Step two -->
<mtr>
<mtd>
<msup>
<mi>a</mi>
<mn>2</mn>
</msup>
<mo>+</mo>
<mn>2</mn>
<mi>a</mi>
<mi>b</mi>
<mo>+</mo>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
</mtd>
<mtd>
<mo>=</mo>
</mtd>
<mtd>
<msup>
<mi>c</mi>
<mn>2</mn>
</msup>
<mo>+</mo>
<mn>2</mn>
<mi>a</mi>
<mi>b</mi>
</mtd>
</mtr>
<!-- Step three -->
<mtr>
<mtd>
<msup>
<mi>a</mi>
<mn>2</mn>
</msup>
<mo>+</mo>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
</mtd>
<mtd>
<mo>=</mo>
</mtd>
<mtd>
<msup>
<mi>c</mi>
<mn>2</mn>
</msup>
</mtd>
</mtr>
</mtable>
</semantics>
</math>
<p>This proof example just added several MathML elements that go beyond simple identifiers and operators. Each of these adds meaning to the expression, which is why assistive technologies can navigate the structure so precisely. For example:</p>
<ul>
<li>
<p><code>mtable</code>, <code>mtr</code> and <code>mts</code>: these directly mirror HTML's <code>table</code>, <code>tr</code> and <code>td</code> but are math-specific. They tell the accessibility tree: "this is a mathematical table with aligned steps," not just a generic layout table. Screen readers can move row-by-row, so each step of the proof becomes navigable.</p>
</li>
<li>
<p><code>mrow</code>: groups expressions together. For example <code>(a + b)</code> is wrapped in an <code>mrow</code> to indicate that the parentheses and the interior form a single unit before exponentiation.</p>
</li>
<li>
<p><code>mfrac</code>: defines an actual mathematical fraction, not just text with a slash. This allows speech engines to say "one half" instead of "one over two" depending on preferences and locale.</p>
</li>
<li>
<p><code>semantics</code>: this is key. It wraps the expression and lets you attach alternative meanings or encodings. Assistive technologies prefer the first child (your visual MathML), but can fall back to the annotation if needed.</p>
</li>
<li>
<p><code>annotation</code>: stores auxiliary information. In this case, the TeX version of the proof. It does not affect the visual rendering in the browser. Instead, it's metadata for tools that consume MathML, like converters, EPUB readers, or braille translators.</p>
</li>
</ul>
<p>Check out how this is announced by different screen readers!</p>
<section aria-labelledby="example-proof">
<h3 id="example-proof">How screen readers interpret the proof</h3>
<details>
<summary>NVDA + Firefox (Windows with MathCAT add-on)</summary>
3 lines
line 1 left parenthesis a plus b right parenthesis squared is equal to c squared plus 4 times 1 half a b
<p>line 2 a squared plus 2 a b plus b squared is equal to c squared plus 2 a b</p>
<p>line 3 a squared plus b squared is equal to c squared</p>
</details>
<details>
<summary>VoiceOver + Safari (Mac)</summary>
Table start, Row 1, Column 1, ( a + b ) squared, Row 1, Column 2, =, Row 1, Column 3, c squared + 4 Ā· ( fraction start, 1 over 2, end of fraction, a b ), Row 2, Column 1, a squared + 2 a b + b squared, Row 2, Column 2, =, Row 2, Column 3, c squared + 2 a b, Row 3, Column 1, a squared + b squared, Row 3, Column 2, =, Row 3, Column 3, c squared, table end, maths
</details>
<details style="margin-bottom: 2rem;">
<summary>VoiceOver + Safari (iOS)</summary>
1 table, table start, Row 1, Column 1, a plus b squared, Row 1, Column 2, equals, Row 1, Column 3, c squared plus 4 dot fraction start 1 over 2, end of fraction, a b, Row 2, Column 1, a squared plus 2 a b plus b squared, Row 2, Column 2, equals, Row 2, Column 3, c squared plus 2 a b, Row 3, Column 1, a squared plus b squared, Row 3, Column 2, equals, Row 3, Column 3, c squared, table end, Math
</details>
</section>
<video style="margin-bottom: 1rem;" title="How VoiceOver interprets the proof formula on Safari" controls="" width="904" height="680">
<source src="https://htmhell.dev/adventcalendar/2025/12/pythagorean-theorem-proof.mp4" type="video/mp4" />
</video>
<p style="margin-top: 1rem;" class="highlight"><strong>Note:</strong> If you want to deepen your understanding in the topic, MDN has a very detailed page about <a href="https://developer.mozilla.org/en-US/docs/Web/MathML/Guides/Proving_the_Pythagorean_theorem">proving the Pythagorean theorem with MathML</a>.</p>
<h2 id="some-a11y-enhancements">Some A11y Enhancements</h2>
<p>We could enhance this by adding an <code>aria-label</code> to a wrapper that provides some information about the following formula, especially when it's a well-known one. By using a <code>section</code> with an <code>aria-label</code> or <code>aria-labelledby</code> together with another element giving the accessible name, we automatically insert a region into the accessibility tree.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>section-1-heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>section-1-heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Pythagorean Theorem<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>math</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1998/Math/MathML<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>math</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>Also, for users who zoom the browser up to 400%, we might want to add a <code>max-width: 100%</code> and <code>overflow-x: auto</code>, so that the formula remains readable, does not break the page and we allow horizontal scrolling only inside the math block, and not at the entire page level.</p>
<h2 id="conveying-mathematical-meaning-with-aria">Conveying mathematical meaning with ARIA</h2>
<p>We also have the <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/math_role"><code>math</code> role</a> from the ARIA specification. With that, we can communicate the mathematical semantics even when we rely on images or non-semantic HTML. However, it does not give good results with VoiceOver on macOS, for example.</p>
<p>As shown on the <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/math_role">MDN page for the <code>math</code> role</a>, we could have:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>math<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>a^{2} + b^{2} = c^{2}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> a<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>sup</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>sup</span><span class="token punctuation">></span></span> + b<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>sup</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>sup</span><span class="token punctuation">></span></span> = c<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>sup</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>sup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<section aria-labelledby="example-div">
<h3 id="example-div">Markup with a div with the math role and how screen readers interpret it</h3>
<details>
<summary>Markup</summary>
<div style="margin-bottom: 2rem;" role="math" aria-label="a^{2} + b^{2} = c^{2}">
a<sup>2</sup> + b<sup>2</sup> = c<sup>2</sup>
</div>
</details>
<details>
<summary>NVDA + Firefox (Windows with MathCAT add-on)</summary>
just announces the text
</details>
<details>
<summary>VoiceOver + Safari (Mac)</summary>
not read, just announces "with 6 items, maths"
</details>
<details style="margin-bottom: 2rem;">
<summary>VoiceOver + Safari (iOS)</summary>
a caret left curly bracket 2 right curly bracket plus b caret left curly bracket 2 right curly bracket equals c caret left curly bracket, Math
</details>
</section>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>pythagorean_theorem.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>a^{2} + b^{2} = c^{2}<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>math<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<section aria-labelledby="example-img">
<h3 id="example-img">Markup with a img with the math role and how screen readers interpret it</h3>
<details>
<summary>Markup</summary>
<img width="150" alt="a^{2} + b^{2} = c^{2}" src="https://htmhell.dev/adventcalendar/2025/12/pythagorean-theorem.png" role="math" />
</details>
<details>
<summary>NVDA + Firefox (Windows with MathCAT add-on)</summary>
just announces the text
</details>
<details>
<summary>VoiceOver + Safari (Mac)</summary>
not read, just announces "maths"
</details>
<details style="margin-bottom: 2rem;">
<summary>VoiceOver + Safari (iOS)</summary>
a caret left curly bracket 2 right curly bracket plus b caret left curly bracket 2 right curly bracket equals c caret left curly bracket, Math
</details>
</section>
<p>In practice, using the math role helps assistive technologies understand that the content is mathematical, but it still doesnāt provide enough semantic detail for them to announce the expression as accurately as MathML does.</p>
<h2 id="the-future-of-mathml">The future of MathML</h2>
<p class="highlight"><strong><abbr title="too long; didn't read">TL;DR:</abbr></strong> MathML Core is what browsers implement today; MathML 4 is the broader language evolving around it.</p>
<p>As I mentioned at the beginning, the origin of MathML was not the web, it was more of a general-purpose specification for browsers, office suites, computer algebra systems, EPUB readers, and LaTeX-based generators, <a href="https://developer.mozilla.org/en-US/docs/Web/MathML">as stated on MDN</a>. MathML Core arose from the need to make it work with web standards, including HTML, CSS, DOM, and JavaScript. Historically, the full MathML spec was broad and partly underspecified for browsers, which led to uneven or incomplete implementations across engines. MathML Core therefore narrows the language to the subset that can be precisely defined on top of the Web Platform, improving testability and cross-browser interoperability. Since June 2025, MathML Core has been a <a href="https://www.w3.org/TR/2025/CR-mathml-core-20250624/">Candidate Recommendation Snapshot</a>. On another note, at the time of this writing, there is <a href="https://www.w3.org/TR/mathml4/">a Working Draft for MathML 4</a>, the next version of MathML. This version aims to be the next "full" spec that extends Core. It keeps the larger feature set (e.g., Content MathML) and adds, among others, the <code>intent</code> attribute so authors can guide screen-reader speech. With it, we'll be able to do something like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>math</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mrow</span> <span class="token attr-name">intent</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>equals(power(a,2)+power(b,2),power(c,2))<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>b<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mo</span><span class="token punctuation">></span></span>=<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mi</span><span class="token punctuation">></span></span>c<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mn</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>msup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mrow</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>math</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>As <a href="https://caniuse.com/mathml">browser support for MathML continues to evolve</a>, previous fallback solutions like <a href="https://github.com/fred-wang/mathml.css">mathml.css</a> are no longer necessary. MathML Core, and soon MathML 4, allow us to express both the visual and semantic meaning of mathematical content without sacrificing accessibility along the way.</p>
<p>Screen-reader support is also steadily improving. Each assistive technology handles MathML in its own way, but the overall trajectory is positive. VoiceOver offers consistent navigation and speech for many common patterns across macOS and iOS. JAWS, especially when paired with Fusion, provides rich support for both speech and braille. And NVDA, which historically required an add-on, is now moving toward a built-in MathCAT integration, making MathML speech and braille support more accessible out of the box for Windows users.</p>
How HTML changes in ePub
2025-12-11T00:00:00Z
https://htmhell.dev/adventcalendar/2025/11/
by Robin Whittleton<br><p><a href="https://www.w3.org/TR/epub-33/">ePub</a> is the W3C standard for ebooks. It lets you take your knowledge of the web, and use it to produce little self-contained sets of documents that can be freely distributed as a single file ready for reading on extremely low-power devices, and they even reflow to fit any screen.</p>
<p>Yet while I said that you can use your knowledge of the web to build ePubs, the technology in use is twisted in unforeseen ways, and you might have to unlearn the things you think you knew. Prepare yourselfā¦</p>
<h2 id="html-sort-of">HTML, sort of</h2>
<p>ePubs, at their core, use HTML, just like the websites we build every day. Except, well, thereās a big asterisk after that. Letās dive into the differences.</p>
<p>A few decades ago <a href="https://www.w3.org/TR/xml/">XML</a> emerged from the pit. XML ā an extensible standard for expressing marked up data ā could be used for documents, data transfer, and a bunch of other things, and people genuinely liked it (or, much like AI today, pretended to for job security). They liked it so much that a concerted effort was started to take HTML and rebuild it on top of XML. This project had a name you might have heard of: <a href="https://www.w3.org/TR/xhtml11/">XHTML</a>.</p>
<p>XHTML didnāt work out, for a number of reasons. The extensibility of XML turned out to not be useful when browsers didnāt support even common extensions. Then there was the problem of fragility: any syntax problems with your XHTML and your users would get a blank screen. If those two problems werenāt enough, XHTML was slower in practice because the browser needed to wait to download the entire document before doing anything else.</p>
<p>But there is one place where XHTML still rules the roost: ePub. ePub books are, at their heart, a collection of XHTML documents (now using <a href="https://html.spec.whatwg.org/#html-vs-xhtml">the XHTML flavour of the HTML Living Standard</a>). This means that:</p>
<ol>
<li>Valid, syntactically correct XML markup is needed. Without that, your e-reader will complain. This means self-closing tags, correct namespaces, XML attributes in the XML namespace (<code>xml:lang</code>), and so on.</li>
<li>Other XML languages can be included directly into XHTML by adding namespaces.</li>
<li>The <code>epub</code> namespace is unlocked, which adds additional functionality to your ePub in e-readers.</li>
</ol>
<p>Weāll come back to thatā¦</p>
<h2 id="css-sort-of">CSS, sort of</h2>
<p>So HTML is actually XHTML in ePub. Is CSS some sort of XCSS? Actually, no: CSS is broadly the same as you know it, but with a few quirks.</p>
<p>First up, e-readers and e-reader software are, compared to our normal evergreen browsers, typically really basic. They can run on underpowered hardware, people often keep their e-readers for over a decade, and the engines they use can be positively historic. To put it another way, Iām wary of using <code>:not()</code> in ePub CSS for a widely distributed title. While I might be overly cautious here, donāt expect nowadays normal pseudoclasses like <code>:is()</code> to have wide support. Luckily, layout tends to be simpler in a document-focused format, and progressive enhancement is possible with <code>@supports</code>.</p>
<p>Next, as our markup is now namespace aware, our CSS needs to follow. For example, if you wanted to style a piece of text in a different language that youāve marked up like:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>As Jean-Paul Sartre said, <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>q</span> <span class="token attr-name"><span class="token namespace">xml:</span>lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fr<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lāenfer, cāest les autres<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>q</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>Then you canāt simply use an attribute selector like <code>q[lang]</code>, you need to define your namespaces and reference them in your selectors using the <code>|</code> separator:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token atrule"><span class="token rule">@namespace</span> xml <span class="token string">"http://www.w3.org/XML/1998/namespace"</span><span class="token punctuation">;</span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">q[xml|lang]</span> <span class="token punctuation">{</span> ⦠<span class="token punctuation">}</span></span></code></pre>
<h2 id="strange-extensions">Strange extensions</h2>
<p>As mentioned earlier, namespace support means that other XML-compatible markup languages can be incorporated directly into your XHTML document. These could be just additional semantic attributes, or even new elements. Obviously to get anything useful out of them you need e-reader support, but thatās present in a few cases. Letās take a look at a couple.</p>
<p>You might actually be familiar with <a href="https://htmhell.dev/adventcalendar/2025/12/">MathML</a>, as itās <a href="https://html.spec.whatwg.org/#mathml">supported in HTML5</a>. The way you use it in HTML is that thereās a broad agreement that the contents of the MathML spec will just work when used in HTML. Adding a basic MathML equation using standard MathML tags (<code>math</code>, <code>mi</code>, <code>mo</code>, <code>mn</code>, and so on) to your normal HTML document ends up looking something like <math alttext="n + 1"><mi>n</mi><mo>+</mo><mn>1</mn></math>.</p>
<p>But in XHTML (because itās an XML language) thereās a standard integration process for any XML language you want to bring in. First, you define your MathML namespace against the root element:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1999/xhtml<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>m</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1998/Math/MathML<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>Then the MathML elements are available to use inside the document under that namespace:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">m:</span>math</span> <span class="token attr-name">alttext</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>n + 1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">m:</span>mi</span><span class="token punctuation">></span></span>n<span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">m:</span>mi</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">m:</span>mo</span><span class="token punctuation">></span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">m:</span>mo</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">m:</span>mn</span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">m:</span>mn</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">m:</span>math</span><span class="token punctuation">></span></span></span></code></pre>
<p>Same goes for SVG: in HTML <a href="https://html.spec.whatwg.org/#svg-0">itās just allowed to be included</a> with standard SVG tags; using <code>svg</code>, <code>title</code> and <code>path</code> can get you something like <svg style="height: 1em" viewBox="0 0 452 452"><title>the HTMHell logo</title><path d="M198.2.94c-28.6 3.3-61.4 14.6-87.3 29.9-61.2 36.2-102.2 99.8-110 170.8-1.8 16-.8 47.3 1.9 62.7 5.3 29.9 17 59.3 33.9 85 68.8 105 209.3 133.9 314 64.5 33.9-22.5 60.3-52.7 78.2-89.5 10.5-21.5 16.5-40.6 20.7-66 2.4-15 2.4-48.2-.1-64.5-7.9-51.9-30.4-95.7-67.7-131.5-35.6-34.2-82.3-56.2-130.1-61.4-11.2-1.3-42.9-1.2-53.5 0m55.2 19.4c14.7 2.1 23.7 4.1 36 8.1 11.9 3.9 34.9 14.5 45.9 21.3l6.9 4.3 7.4-5.2c4.1-2.9 7.6-5 7.9-4.7.3.2-.7 4-2.1 8.3-1.3 4.4-2.5 8.2-2.5 8.5s4.9 4.9 10.9 10.2c48.3 42.8 74.5 109.7 68.1 173.8-.9 8.8-4.2 26.9-6.5 36-.6 2.3.1 3 7.1 8l7.7 5.4-9.6.3-9.6.3-1.9 5.2c-11.5 30.5-33.6 61.5-59 82.8-32.1 26.8-70.7 43.8-109.2 47.9-5.8.6-12.4 1.3-14.7 1.6l-4.2.5-2.7 8.2c-1.4 4.5-3 8.5-3.3 9-.4.4-2-3.3-3.6-8.2l-2.8-9-4.6-.5c-2.5-.3-7.7-.8-11.6-1.1-18.2-1.6-42.2-8.1-62.2-16.9-47.2-20.6-88.8-63.8-107.4-111.1-1.3-3.3-2.7-6.5-3.2-7.2-.6-.8-4.3-1.3-10-1.5l-9.1-.3 7.6-5.7 7.6-5.7-1.8-6.8c-5.3-20.1-7.5-52.2-5.1-73.4 5.3-45.8 24.7-87.2 56.5-120.6 5.4-5.7 12.7-12.6 16.2-15.4 5.4-4.3 6.3-5.5 5.8-7.3-3.9-11.8-5.1-16.5-4.1-16 .7.4 4.2 2.9 7.9 5.6 3.7 2.6 7 4.8 7.4 4.8.3 0 2.4-1.3 4.7-2.9 6.9-4.7 20.5-11.7 32.2-16.6 17.5-7.3 36.5-12.3 57-14.8 8.3-1.1 40.9-.5 50 .8"></path><path fill="#d72b2b" d="M165.9 218.04c-3 2.1-5.9 4.3-6.3 4.8-.5.4 14.1 47 32.4 103.4s33.7 102.2 34 101.8c.8-.7 42.9-129.9 42.9-131.3 0-.5-3.5-.9-7.8-.9h-7.8l-13.5 41.5c-7.4 22.8-13.7 41.5-14 41.5s-12.4-36.6-26.9-81.3c-14.5-44.6-26.6-81.7-26.9-82.3-.4-.8-2.5.2-6.1 2.8m137.7-4.7-2.5 7.5 35.4 25.7 35.5 25.8-86.6.3c-65.4.1-86.5.5-86.5 1.4 0 .6 1 4.2 2.2 8l2.2 6.8h217.8l-57.1-41.5c-31.5-22.8-57.3-41.5-57.5-41.5s-1.6 3.4-2.9 7.5m-184.8 11.3-87.6 63.7 70 .3c38.5.1 70.2 0 70.5-.2.2-.3-.7-3.8-1.9-7.8l-2.4-7.3-43.9-.5-44-.5 68.5-49.7c37.6-27.3 69.2-50.3 70.2-51 1.7-1.2 1.3-1.8-4.8-6.1-3.6-2.6-6.6-4.7-6.8-4.7-.1.1-39.6 28.8-87.8 63.8m169.8-123.5-56.7 41.2 2.7 2.3c1.6 1.2 4.5 3.4 6.5 4.8l3.8 2.7 6.7-5c37.2-27 64.4-46.5 64.7-46.2.1.2-11.8 37.5-26.6 82.9-14.7 45.4-26.8 82.6-26.8 82.8s3.6.2 8.1 0l8.1-.3 33.4-102.9c18.4-56.6 33.3-103 33.2-103.2-.2-.1-25.9 18.3-57.1 40.9m-182.7-40.8c0 1.1 42 130.7 42.9 132.3.5.9 2.5-.1 6.9-3.4 3.4-2.5 6.2-4.9 6.2-5.2 0-.4-6.1-19.1-13.5-41.7-7.4-22.5-13.2-41.1-13-41.3s31.6 22.4 69.9 50.2 70 50.6 70.4 50.6c.7 0 5.2-12.4 5.2-14.3 0-.3-37.9-28.2-84.2-61.8-46.4-33.7-85.8-62.3-87.5-63.7-1.8-1.3-3.3-2.1-3.3-1.7"></path></svg>. But in XHTML youāll need to declare the SVG namespace and use that with your images:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1999/xhtml<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>svg</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">svg:</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 452 452<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">svg:</span>title</span><span class="token punctuation">></span></span>the HTMHell logo<span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">svg:</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">svg:</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M198.2.94c-28.6 3.3-61.4ā¦<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token namespace">svg:</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<p>Of course, weāre talking about ePub here. Thereās a <a href="https://www.w3.org/TR/epub-33/">W3 ePub specification (currently at version 3.3)</a> that defines the structure and metadata of an ePub document, and gives us the <a href="https://www.w3.org/TR/epub-33/#dfn-epub-type"><code>epub:type</code> attribute</a>. This attribute (in conjunction with the <a href="https://www.w3.org/TR/epub-ssv-11/">Structural Semantics Vocabulary specification</a>) can be used in a few ways to improve your collectionās usability within an e-reader. Letās look at an example.</p>
<p>One thing books often have is endnotes, but thereās no easy way of expressing that semantic state in HTML. In ePubās vocabulary though we find the <a href="https://www.w3.org/TR/epub-ssv-11/#noteref"><code>noteref</code> attribute value</a>. If we use this on a link then readers know to pull in a fragment from another place and typically display then this inside a modal that can be dismissed to return to the existing place. This looks something like:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>ePub is hellish.<span class="token tag"><span class="token tag"><span class="token punctuation"><</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>endnotes.xhtml#note-1<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>noteref-1<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">epub:</span>type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>noteref<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>And in your collection of endnotes:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</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>note-1<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">epub:</span>type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>endnote<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>ā¦unless you read HTMHell. <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>page.xhtml#noteref-1<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">epub:</span>type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>backlink<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ā©<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight"><strong>Note:</strong> <code>epub:type</code> is gradually being deprecated in favour of the <code>role</code> values defined in the <a href="https://www.w3.org/TR/dpub-aria-1.1/">Digital Publishing WAI-ARIA spec</a>, but currently that functionality is either unimplemented or only available in the latest systems. For example, the above endnotes functionality doesnāt yet work in Apple Books (generally a good e-reader) if you use <code>role="doc-noteref"</code> and <code>role="doc-endnotes"</code>.</p>
<p>As well as these ePub semantic vocabulary, we also have things like the <a href="https://www.daisy.org/z3998/2012/vocab/structure/">Z39.98-2012 Structural Semantics Vocabulary</a>. This extends the base ePub spec, and takes the form of <code>z3998</code>-namespaced attribute <em>values</em>, which definitely looks odd if youāre not used to it. You can use these to express even more fine-grained semantic values, for example <a href="https://www.daisy.org/z3998/2012/vocab/structure/#roman">Roman numerals</a> (<code><span epub:type="z3998:roman">DCLXVI</span></code>) or <a href="https://www.daisy.org/z3998/2012/vocab/structure/#h_letters">parts of letters</a>. Broad support for extracting any functionality from these is extremely lacking, but when did that ever stop us front-end developers from going over the top?</p>
<h2 id="want-to-try-this-all-out?">Want to try this all out?</h2>
<p>Writing an ePub isnāt exactly hard, but thereās some scaffolding necessary to get to a workable structure. You need a <a href="https://www.w3.org/TR/epub-33/#sec-container-metainf-container.xml"><code>container.xml</code> file</a> in a <code>META-INF</code> directory that points at <a href="https://www.w3.org/TR/epub-33/#sec-package-doc">a package file</a>. The package file contains a bunch of metadata about your ePub, including <a href="https://www.w3.org/TR/epub-33/#sec-pkg-manifest">a manifest</a> of the XHTML files in your book (for example, different chapters), and <a href="https://www.w3.org/TR/epub-33/#sec-pkg-spine">a spine</a> describing the order they should be shown to the reader. You then add your XHTML files, reference them in the manifest and spine, and finally zip the whole directory up into a single archive and rename it to a <code>.epub</code>.</p>
<p>To get you started, Iāve prepared <a href="https://htmhell.dev/adventcalendar/2025/11/htmhell-2025-12-11.epub">a copy of this blog post as an ePub</a> ready for your favourite e-reader (assuming itās modern: I havenāt included any compatibility hacks). To inspect the contents we just need to unzip it; try renaming the file to have a <code>.zip</code> at the end and opening it with your favourite unarchiver. The markup can be found in <code>src/epub/text</code>.</p>
<p>If you want to create your own ePubs Iād personally recommend starting with <a href="https://github.com/standardebooks/tools">the Standard Ebooks toolset</a>: it can create unbranded ePubs and has a bunch of compatibility tooling built in. Once installed, you can create a directory with the requisite scaffolding with <code>se create-draft --white-label</code>, and then build that into an ePub with <code>se build</code>.</p>
<p>I hope you have fun putting your own ebooks together! Itās a useful new skill that reuses a lot of your existing knowledge.</p>
a11y freedom beaver
2025-12-10T00:00:00Z
https://htmhell.dev/adventcalendar/2025/10/
by Steve Faulkner<br><p><span role="img" aria-label="Accessibility Freedom Beaver">āæā®ļøš¦«</span> sez: <a href="https://html5accessibility.com/stuff/2020/11/24/know-your-standards/">Know your Standards</a></p>
<p><img src="https://github.com/user-attachments/assets/bab8c885-0635-4a0c-82b7-ccb3004c1b95" alt="SAY 'ARIA TAG' AGAIN Side profile pic of Adrian looking menacing with a mouse cursor 'tattoo' below his eye" width="297" height="398" /></p>
<h2 id="1st-rule-of-aria">1st Rule of ARIA</h2>
<p>The <strong>first rule</strong> originally appeared in 2012 <a href="https://www.tpgi.com/html5-accessibility-chops-using-aria-notes/">Notes on using ARIA</a>, it was then moved to a new home <a href="https://w3c.github.io/using-aria/#rule1">Using ARIA</a>.</p>
<p>It's not really a <strong>rule</strong> in any formal sense; I just made it up. It has remained unchanged for 14 years but remains relevant today.</p>
<blockquote class="blockquote-no-quotes">
<p><strong>First Rule of ARIA Use</strong></p>
<p>If you can use a native HTML element <a href="https://html.spec.whatwg.org/multipage/">HTML</a> or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.</p>
<p><strong>Under what circumstances may this not be possible?</strong></p>
<ul>
<li>If the feature is available in <a href="https://html.spec.whatwg.org/multipage/">HTML</a> but it is not implemented or it is implemented, but accessibility support is not</li>
<li>If the visual design constraints rule out the use of a particular native element, because the element cannot be styled as required</li>
<li>If the feature is not currently available in <a href="https://html.spec.whatwg.org/multipage/">HTML</a></li>
</ul>
</blockquote>
<p>source: <cite><a href="https://w3c.github.io/using-aria/#rule1">Using ARIA</a></cite></p>
<h3 id="reasons-to-be-cheerful-(about-not-having-to-use-aria-so-much)">Reasons to be cheerful (about not having to use ARIA so much)</h3>
<blockquote>
<p>the feature is available in <a href="https://html.spec.whatwg.org/multipage/">HTML</a> but it is not implemented or it is implemented, but accessibility support is not</p>
</blockquote>
<p>When I first wrote the first rule there were many new features in HTML, many of which had not had their accessibility support (or support in general) wired up. Which is why I started documenting support for new features back in 2010 (<a href="https://web.archive.org/web/20101011095344/http://www.html5accessibility.com/">HTML5Accessibility.com WayBack Machine snapshot October 2010</a>). Last time I checked back in <a href="https://html5accessibility.com/">August 2020</a>, things were looking much better. So these days there is little to no need to use ARIA to supplement native HTML semantics, the browsers do it for you <em>mostly</em>. In fact the <strong>unecessary use of ARIA</strong> is a HTML conformance error as defined in the <a href="https://w3c.github.io/html-aria/#docconformance">ARIA in HTML</a> specification.</p>
<blockquote>
<p>visual design constraints rule out the use of a particular native element, because the element cannot be styled as required</p>
</blockquote>
<p>In 2025 the opportunity for a front end developer to craft visual UI that meets the needs of their bosses is much improved. CSS and browser implementations continue to be enhanced to make it possible.</p>
<blockquote>
<p>the feature is not currently available in <a href="https://html.spec.whatwg.org/multipage/">HTML</a></p>
</blockquote>
<p>This is still a valid reason for building stuff from HTML elements that carry little or no semantics, then adding ARIA to convey meaning robustly and interoperably to people that make use of assistive technology to render web content understandable. But valid <em>less so</em> as many of the features that are routinely foisted upon users are now available as native HTML, for example, <a href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">dialogs</a> and <a href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-details-element">disclosures</a></p>
<h2 id="what-aria-does-not-do">What ARIA does not do</h2>
<p>The good thing about using HTML elements is they have operability built in: <strong>ARIA does not</strong>.<br />
For example, a <code><button></code>-element is automatically included in the focus order and will have event listeners attached, an element with <code>role="button"</code> will <em>not</em> have those characteristics by default.<br />
Please read <a href="https://html5accessibility.com/stuff/2024/07/15/what-aria-still-does-not-do/">What ARIA still does not do</a> for details.<br />
A popular use case for ARIA is custom elements, to appreciate what you need to do to make them accessible, the <a href="https://www.tpgi.com/web-components-punch-list/">Web Components punch list</a> may be helpful.</p>
<h2 id="last-words">Last words</h2>
<p>We <cite><a href="https://webaim.org/projects/million/#aria">already know</a></cite>:</p>
<blockquote>
<p>Increased ARIA usage on pages was associated with higher detected errors. The more ARIA attributes that were present, the more detected accessibility errors could be expected.</p>
</blockquote>
<p>Do not introduce more ARIA into your code in an effort to please the knowledge ghouls of OpenAI and their <a href="https://html5accessibility.com/stuff/2025/10/23/atlas-ableism/">new browser</a>. Although you wouldn't know it from the <a href="https://adrianroselli.com/2025/10/openai-aria-and-seo-making-the-web-worse.html"><em>bumpf</em> OpenAI published</a> - <strong>It is not needed</strong>.</p>
<p>and a merry fucking xmas to all!</p>
Discover Dialog
2025-12-09T00:00:00Z
https://htmhell.dev/adventcalendar/2025/9/
by Sara Joy<br><p>Suffering modal woes? Positioning, backdrops, focus trapping, z-index ā Oof.</p>
<p>Just as I was, some of you may have been coding a lot of these functions into a <code><div></code> by hand with CSS and JavaScript, or using a library to handle them for you.</p>
<p>Either way, things are getting simpler and more declarative all the time - and when we're so busy working, we don't always realise how much has changed! Maybe this will help you shave off some lines of code or remove some dependencies from your codebase.</p>
<p>To those of you new to building on the web, this is for you too, as you lucky things get to skip the old ways :)</p>
<h2 id="meet-lessdialoggreater">Meet <button id="openMeetDialog"><dialog></button></h2>
<dialog id="meetDialog" closedby="any">
<p>
Hello š
<br />Nice to meet you!
</p>
<form method="dialog">
<button>Close</button>
</form>
</dialog>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog">dialog element</a> has been available across browsers since March 2022, and allows you to pop a little overlay on your page - a common variant is known as a modal - and it has a whole bunch of abilities built in, with further useful facets still being added.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</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>meetDialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Hello š</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span> <span class="token punctuation">/></span></span>Nice to meet you!</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Close<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dialog</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="modal-and-non-modal">Modal and non-modal</h2>
<p>We're probably more familiar with the modal variety of dialog, intended to (hopefully only briefly) stop you interacting with the page under it. But there are also plenty of non-modal dialogs to be seen around the web, for example <button id="openToastDialog">toast</button> notifications.</p>
<dialog id="toastDialog" closedby="any" style="
margin: 0;
border-width: 0.25em 1em 1em 0.25em;
border-style: solid;
border-radius: 1.5em 2em 0.5em 1em;
border-color: saddlebrown;
background-color: burlywood;
box-shadow: 1em 1em 1.5em -0.5em #8888;
rotate: 10deg;
position: fixed;
left: 3em;
bottom: 3em;
z-index: 10;
">
<p>
Pop! Do I look
<br /> a little burned?
</p>
<form method="dialog">
<button>Close</button>
</form>
</dialog>
<p>The <code>dialog</code> element gives us both modal and non-modal variants, depending on how we open them. In the case of more than one dialog being opened (assuming no close action happening on the previous dialogs), the most recently opened dialog will appear on top.</p>
<h2 id="opening-with-js">Opening with JS</h2>
<p>To open a dialog, we use one of two JavaScript functions on a dialog element:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"><span class="token punctuation">.</span><span class="token function">showModal</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span></code></pre>
<p>Likely based on the click of a button, you will run something like the following (you will likely use a much more specific <code>querySelector</code>):</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"dialog"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token comment">// or</span></span><br /><span class="highlight-line">document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"dialog"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">showModal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p class="highlight">š”
There is a declarative method available to <a href="https://htmhell.dev/adventcalendar/2025/7/">invoke</a> dialogs, but for now I will stick to what is currently available across the main browsers.
</p>
<p>In the CodePen below, both kinds of dialog opening method are available. You'll notice that you can still interact with the Open Modal button after having opened the dialog, but not vice-versa. The modal can be closed with the Esc key, the non-modal dialog cannot.</p>
<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="ZYQvJQG" data-pen-title="Dialogs!" data-editable="true" data-user="sarajw" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/sarajw/pen/ZYQvJQG">
Dialogs!</a> by Sara (<a href="https://codepen.io/sarajw">@sarajw</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://public.codepenassets.com/embed/index.js"></script>
<h2 id="closing-dialogs">Closing dialogs</h2>
<p>You'll also not see any JS for the closure of the dialogs. Pop into the HTML tab in the demo, and you might spot the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/form#method"><code>method="dialog"</code></a> in the <code><form></code> element wrapping the <code><button></code>. Here I'm only using it to close the dialogs with said button. If you include more inputs here, <code>method="dialog"</code> will send a submit event and close the dialog, but not actually submit any data anywhere.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Close<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>Dialogs can also be closed without using this form method, instead by activating some JS (maybe also with a button):</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"dialog"</span><span class="token punctuation">)</span><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></span><br /><span class="highlight-line"><span class="token comment">// or</span></span><br /><span class="highlight-line">document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"dialog"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">requestClose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>This <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/requestClose"><code>requestClose()</code></a> is a newly available addition, allowing access to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/cancel_event">cancel event</a>, otherwise thrown before the <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close_event">close event</a> when exiting a modal with the <code>Esc</code> key. This may be useful to create <em>"are you sure?"</em> type interactions. (Please do so with care if you must! We'll have no deceptive patterns here..)</p>
<h3 id="light-dismissal">Light dismissal</h3>
<p>You're probably already trying to close the dialogs by clicking outside of them, which is a common pattern and is often referred to as <a href="https://html.spec.whatwg.org/dev/interactive-elements.html#dialog-light-dismiss"><em>light dismiss</em></a>.</p>
<p>This isn't available by default, but can be coded in with CSS and JS.</p>
<p>With thanks to <a href="https://gomakethings.com/revisiting-how-to-dismiss-native-html-dialog-elements-when-the-backdrop-is-clicked/">Chris Ferdinandi</a> and <a href="https://bsky.app/profile/konnorrogers.com/post/3lxczan6dh22b">Konnor Rogers</a> for the inspiration, I find the following code works well for <strong>modal</strong> dialogs only, through the magic of the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::backdrop"><code>::backdrop</code></a>:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* disallow pointer events on the backdrop, so they<br /> don't register as clicks on the dialog itself */</span><br /><span class="highlight-line"><span class="token selector">dialog::backdrop</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">pointer-events</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<pre class="language-js"><code class="language-js"><span class="highlight-line">document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// find any open modal dialogs</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> openDialogs <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"dialog[open]:modal"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">// return if none open</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>openDialogs<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// check for clicks on documentElement, passed through the backdrop</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>target <span class="token operator">===</span> document<span class="token punctuation">.</span>documentElement<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// close the open dialogs</span></span><br /><span class="highlight-line"> openDialogs<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">dialog</span><span class="token punctuation">)</span> <span class="token operator">=></span> dialog<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><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="yyepzaX" data-pen-title="Dialogs with light dismiss (JS)" data-editable="true" data-user="sarajw" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/sarajw/pen/yyepzaX">
Dialogs with light dismiss (JS)</a> by Sara (<a href="https://codepen.io/sarajw">@sarajw</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://public.codepenassets.com/embed/index.js"></script>
<p class="highlight">š”
You may have spotted we selected <code>dialog[open]</code> in the JS above.
<br />
<br />Yes, open dialogs do get <code>open</code> added as an attribute, and it is <em>possible</em> to toggle this to open and close a dialog, but it is <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/open">not recommended</a>. It will always open as a non-modal dialog when done in this way, and may lead to confused or missing <code>close</code> events.
</p>
<p>I am selecting <code>:modal</code> dialogs only in the code and demonstration above - but you can omit this if you want the click-away to also close any <em>non</em>-modal dialogs that happen to be open at the same time.</p>
<p>To achieve light dismiss on non-modal dialogs alone is trickier unfortunately, as they have no backdrop to play with...</p>
<p>But there is some light at the end of the dismiss tunnel with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/closedBy"><code>closedby</code></a> property, which is very nearly available across the big browsers - so please feel free to poke any friendly Safari developers you might know! Then it could look as simple as the below:</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="myVNLmb" data-pen-title="Dialogs with light dismiss (closedby)" data-editable="true" data-user="sarajw" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/sarajw/pen/myVNLmb">
Dialogs with light dismiss (closedby)</a> by Sara (<a href="https://codepen.io/sarajw">@sarajw</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://public.codepenassets.com/embed/index.js"></script>
<h2 id="(some)-styling-built-in">(Some) styling built in</h2>
<p>Apart from limiting the piratey <em>lorem ipsum</em> column width and centering it, no other custom styling has occurred in the CodePen demos.</p>
<p>By default, the non-modal dialog has no backdrop, while the modal dialog's <code>::backdrop</code> has a subtle transparent grey, and the dialog itself is centered vertically as well as horizontally on the page.</p>
<p>I am going to stand on the shoulders of giants here, and suggest some places you can go to look at amazing dialog CSS styling and animation:</p>
<ul>
<li><a href="https://nerdy.dev/have-a-dialog">Have a dialog by Adam Argyle at nerdy.dev</a></li>
<li><a href="https://css-tricks.com/getting-creative-with-html-dialog/">Getting Creative With HTML Dialog by Andy Clarke at css-tricks.com</a></li>
<li><a href="https://frontendmasters.com/blog/animating-dialog/">Animating the Dialog Element by Matthew Morete at frontendmasters.com</a></li>
</ul>
<h2 id="scrollin-scrollin-scrollin">Scrollin', scrollin', scrollin'...</h2>
<p>That by default the website under your modal is still scrollable may or may not bother you. It bothers <em>me</em>!</p>
<p>There are ways and means to stop that happening, thankfully.</p>
<p>A classic and elegant way made possible with the sort-of-new-but-well-supported <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:has"><code>:has()</code></a> pseudo-class and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/scrollbar-gutter"><code>scrollbar-gutter</code></a> property is the following:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* Check whether any modal dialogs are open */</span></span><br /><span class="highlight-line"><span class="token selector">html:has(dialog[open]:modal)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* Poof! No more scrolling! */</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* keep the scrollbar width */</span></span><br /><span class="highlight-line"> <span class="token property">scrollbar-gutter</span><span class="token punctuation">:</span> stable<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The <code>scrollbar-gutter</code> property keeps the text on the page from reflowing with a jerk, when it suddenly becomes one scrollbar-width wider as the rest of the page beyond the viewport is hidden.</p>
<p class="highlight">š”
If you have both non-modal and modal dialogs on your page, be sure to use <code>:modal</code> in your selector so this only triggers on open <em>modal</em> dialogs.
</p>
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="OPMzjBY" data-pen-title="Modal dialog with scroll-stop" data-editable="true" data-user="sarajw" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/sarajw/pen/OPMzjBY">
Modal dialog with scroll-stop</a> by Sara (<a href="https://codepen.io/sarajw">@sarajw</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://public.codepenassets.com/embed/index.js"></script>
<p>You may have noticed - if you can see a scrollbar - that the scrollbar gutter isn't taking on the backdrop style. Yeah. Go ahead and comment out the <code>scrollbar-gutter: stable;</code> line in the CSS, open/close the modal a few times, and see whether the scrollbar appearing and disappearing bothers you more. It might not - but it bothers <em>me</em>!</p>
<p>Reinstate <code>scrollbar-gutter: stable;</code>, then scroll down a little in the CSS of the above CodePen and uncomment the following:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">dialog[open]::backdrop</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> transparent<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">backdrop-filter</span><span class="token punctuation">:</span> <span class="token function">blur</span><span class="token punctuation">(</span>0.25rem<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Here I've dropped the default shading of the <code>::backdrop</code> and applied a blur instead - meaning the gutter blends in, assuming the gutter and page are both the same colour. You might even have some fun seeing how the scrollbars change with <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/color-scheme"><code>color-scheme</code></a>...</p>
<p>Honestly though, different browsers and OSes and dark and light modes and device types make scroll bars look different all over the place.</p>
<p>On a MacBook whether you even <em>see</em> the scrollbar when you're not actively scrolling depends on whether you have an external pointing device connected or not! So many people now browse the web with mobile devices, where the scrollbars and gutters are largely hidden - so it likely isn't worth spending too much effort trying to "fix" this. Trust me. Don't waste your time on it, I've wasted enough for both of us!</p>
<h2 id="accessibility">Accessibility</h2>
<p>As it opens, by default the focus will land on either the whole dialog, or if it has one, the first focusable element (whether it's a close button or not). A sighted user with a pointing device will probably not tend to notice where the focus lands - but it's very important as a screen reader user, as the focus determines where in a document the reader lands and begins reading from.</p>
<p>In case of needing the focus to land somewhere specific for your use case, I heartily recommend reading <a href="https://adrianroselli.com/2025/06/where-to-put-focus-when-opening-a-modal-dialog.html?Theme=Light">Where to Put Focus When Opening a Modal Dialog</a> by Adrian Roselli to help you decide where the focus will land on your dialog. Then read <a href="https://www.matuzo.at/blog/2023/focus-dialog/">O dialog focus, where art thou?</a> to help you consistently get it where you want it.</p>
<p class="highlight">
If you read the MDN page on dialogs and are bothered by <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#attributes">this warning about <code>tabindex</code> on <code><dialog></code></a>, you may either take Adrian or Manuel's word that it's OK, or you can enjoy Manuel's deep dive into <a href="https://www.matuzo.at/blog/2025/whats-an-interactive-element">What's an interactive element?</a>. Enjoy!
</p>
<h2 id="dialogs-discovered">Dialogs discovered</h2>
<p>I hope this has been enough to convince you that it's worth using dialogs, if you aren't already, or maybe you've found a couple of interesting tidbits you otherwise weren't aware of.</p>
<p>HTML is still changing and improving - it's not at all archaic or just there to provide <div>s into which we can inject all the things with JavaScript (though of course you can still do that, just please inject accessible elements)!</p>
<p>Feel free to contact me on the <a href="https://sarajoy.dev/#find">social medias</a> to start a, uh, dialog.</p>
<script>
const meetDialogButton = document.getElementById("openMeetDialog");
const meetDialog = document.getElementById("meetDialog");
meetDialogButton.addEventListener("click", () => meetDialog.showModal());
const toastDialogButton = document.getElementById("openToastDialog");
const toastDialog = document.getElementById("toastDialog");
toastDialogButton.addEventListener("click", () => toastDialog.show());
</script>
What's wrong with this HTML, and is it valid?
2025-12-08T00:00:00Z
https://htmhell.dev/adventcalendar/2025/8/
by Patrick Brosset<br><p>Behold this magnificient HTML document:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span> <span class="token attr-name">marginheight</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>150</span> <span class="token attr-name">marginwidth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>300</span> <span class="token attr-name">bgcolor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>black</span> <span class="token attr-name">text</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>white</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>marquee</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>Hello <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>HTML<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span> World!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>marquee</span><span class="token punctuation">></span></span></span></code></pre>
<p>To try it in your browser, copy the following line and paste it into the address bar of your browser:</p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line">data:text/html,<span class="token operator"><</span>html<span class="token operator">></span><span class="token operator"><</span>body <span class="token assign-left variable">marginheight</span><span class="token operator">=</span><span class="token number">150</span> <span class="token assign-left variable">marginwidth</span><span class="token operator">=</span><span class="token number">300</span> <span class="token assign-left variable">bgcolor</span><span class="token operator">=</span>black <span class="token assign-left variable">text</span><span class="token operator">=</span>white<span class="token operator">></span><span class="token operator"><</span>marquee<span class="token operator">></span><span class="token operator"><</span>b<span class="token operator">></span>Hello <span class="token operator"><</span>i<span class="token operator">></span>HTML<span class="token operator"><</span>/b<span class="token operator">></span> World<span class="token operator">!</span><span class="token operator"><</span>/i<span class="token operator">></span><span class="token operator"><</span>/marquee<span class="token operator">></span></span></code></pre>
<h2 id="whats-wrong-with-it?">What's wrong with it?</h2>
<p>Everything? I mean, this HTML looks like it was written in 1998!</p>
<ol>
<li>
<p>The document is in <em>quirks mode</em> because it lacks a proper <a href="https://developer.mozilla.org/docs/Glossary/Doctype"><code>DOCTYPE</code> preamble</a>.</p>
<p>If you've never heard of quirks mode, then you're probably lucky enough to have started your web development career after it was an important thing to know about. Suffice to say it's weird. Here are some of the ways quirks mode impacts (or impacted) HTML documents:</p>
<ul>
<li>
<p>The box model used to behave differently in older browsers, which affected layout and spacing. For example, in quirks mode, Internet Explorer included the padding and borders in the element's total width and height. Today, all browsers apply width and height to the content box of an element by default, unless you change the <code>box-sizing</code> CSS property.</p>
<p><img src="https://htmhell.dev/adventcalendar/2025/8/box-sizing-differences.png" alt="A diagram showing the difference between the box model in quirks mode and standards mode." /></p>
</li>
<li>
<p>Font sizes don't inherit on table elements.</p>
</li>
<li>
<p>Certain inline elements, such as images, don't vertically align the way you think they should when they're the only element inside a block-level container.</p>
</li>
</ul>
<p>You can see a live example of some of these quirks on my site at <a href="https://patrickbrosset.com/lab/quirksmode/">Quirks mode vs Standards mode</a>.</p>
</li>
<li>
<p>The <code><head></code> tag is missing, which means the document has no <code><title></code> either, which is bad for accessibility and UX in general</p>
<p>A common thing that assistive technology users do is read the title of a page first to know if they want to spend more time reading the page's content. Without a descriptive title, folks are forced to start reading more of the content to know if that's what they were looking for in the first place, which is time-consuming and potentially confusing.</p>
<p>In addition, a title is also useful for SEO purposes, is displayed in browser tabs, used when bookmarking pages, and more.</p>
</li>
<li>
<p>The <code><body></code> tag uses deprecated attributes: <code>marginheight</code>, <code>marginwidth</code>, <code>bgcolor</code>, and <code>text</code>.</p>
<p>These attributes are <a href="https://html.spec.whatwg.org/multipage/obsolete.html#obsolete">obsolete and discouraged by the spec itself</a>.</p>
</li>
<li>
<p>The <code><marquee></code> tag is <a href="https://html.spec.whatwg.org/multipage/obsolete.html#the-marquee-element">obsolete</a> and should be avoided in favor of CSS animations.</p>
<p>Plus, if you really must animate scrolling text, then please use the <a href="https://developer.mozilla.org/docs/Web/CSS/@media/prefers-reduced-motion"><code>prefers-reduced-motion</code> media query</a> to respect user preferences.</p>
</li>
<li>
<p>The <code><b></code> and <code><i></code> tags look like they're used for styling. That's wrong, right?</p>
<p>More on that later.</p>
</li>
<li>
<p>The <code><b></code> and <code><i></code> tags are improperly nested. The nesting is <code><b><i></b></i></code> which is out of order.</p>
</li>
<li>
<p>The closing <code></body></code> and <code></html></code> tags are missing.</p>
</li>
</ol>
<h2 id="is-this-valid-html?">Is this valid HTML?</h2>
<p>Well, yes and no:</p>
<ul>
<li>No: if you send this to the <a href="https://validator.w3.org/nu/">W3C HTML validator</a>, it'll be pretty angry at you and will list the errors I mentioned earlier.</li>
<li>But also, yes: the resulting page just loads and works fine in browsers. See for yourself:</li>
</ul>
<p><img src="https://htmhell.dev/adventcalendar/2025/8/browser-rendering.png" alt="A browser window showing the live webpage, which has a black background, and the phrase Hello HTML World! in white." /></p>
<p>Before discussing each point in details, don't you think this is just beautiful? HTML is so self-correcting that making a browser fail only by using HTML is really hard to achieve, and HTML that looks like it was written two decades ago still works! I mean, take a look at <a href="https://www.spacejam.com/1996/">spacejam.com</a>, <a href="https://www.thecrystalcornerbar.com/">this old bar website</a>, or even <a href="https://info.cern.ch/hypertext/WWW/TheProject.html">the very first web page that was ever created</a>.</p>
<p>Now let's go over the list of issues I mentioned earlier one more time, but this time, let's talk about why they're not actually causing any problems:</p>
<ol>
<li>
<p>Sure, quirks mode can lead to weird rendering issues if you don't know that you're using it, but it's still implemented in browsers and perfectly ok to use.</p>
<p>Even if quirks mode was added for <a href="https://quirks.spec.whatwg.org/#history">historical reasons</a>, to support web pages that were made before the CSS specification was fully fleshed out, the code in browser engines, which detects the document mode and renders it accordingly, is here to stay.</p>
<p>There really is no reason for browsers to ever remove it, unless one day, all quirks mode documents were to disappear from the web. This seems highly unlikely though. Judging by Chrome's <a href="https://chromestatus.com/metrics/feature/timeline/popularity/2034">QuirksModeDocument usage metric</a>, about 30% of all page loaded in Chrome still use quirks mode! A bunch of the sites that are listed on that usage metric page appear to be using it from iframes created to display ads. Still, that's a lot of page loads.</p>
<p>If you're encountering weird rendering issues that you can't explain, double check that you have a <code>DOCTYPE</code> in your HTML document. You can also run the following line of code in the browser console: <code>document.compatMode</code>. If it returns <code>BackCompat</code>, then you're in quirks mode.</p>
</li>
<li>
<p>The <code><head></code> tag can definitely be omitted. Neither the <a href="https://html.spec.whatwg.org/multipage/semantics.html#the-head-element">HTML specification</a>, nor browser implementations require the tag to be present.</p>
<p>It's bad for accessibility reasons if you omit it, again because you probably also won't have a <code><title></code> tag, but it still works.</p>
<p>In fact, you can also omit <code><html></code> and <code><body></code> tags too. Personally, I commonly use this to quickly test things out in the browser. Instead of creating a new HTML file on my computer, which takes a bit more time, I just type some HTML in the address bar directly. For example: <code>data:text/html,<div>something</code>. No <code><html></code>, no <code><head></code>, no <code><body></code> elements.</p>
</li>
<li>
<p><code>marginheight</code>, <code>marginwidth</code>, <code>bgcolor</code>, or <code>text</code> are deprecated <em>presentational attributes</em>. But, even if they're deprecated and discouraged, they're still implemented in browsers, for backward compatibility reasons.</p>
<p>In fact, here are other similar attributes: <code>bgColor</code>, <code>fgColor</code>, <code>linkColor</code>, <code>alinkColor</code>, and <code>vlinkColor</code>.</p>
<p>If you're as old as I am, you might have used these attributes a long time ago, perhaps when creating sites in FrontPage or Dreamweaver.</p>
<p>Anyway, these presentational attributes act as 0-specificity CSS properties, which means that any CSS property you set in a stylesheet will override them.</p>
</li>
<li>
<p>The <code><marquee></code> element still animates text in browsers. In fact, if you want to go crazy with it, try nesting two <code><marquee></code> elements, like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>marquee</span><br /><span class="highlight-line"> <span class="token attr-name">direction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>down<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">behavior</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>marquee</span> <span class="token attr-name">behavior</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>This text will bounce<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>marquee</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>marquee</span><span class="token punctuation">></span></span></span></code></pre>
<p>Take a look at <a href="https://codepen.io/captainbrosset/pen/dPGvrMQ?editors=1100">the example on codepen</a>.</p>
<p>For an accessible alternative, see Daniela Kubesch's article <a href="https://www.htmhell.dev/adventcalendar/2022/15/">Get that marquee āØAeStHeTiCāØ</a>.</p>
</li>
<li>
<p>Using <code><b></code> and <code><i></code> is perfectly valid. They used to be meant for making the text bold and italic, hence their names. But they were deprecated in HTML4, and the meaning of the tags was changed to mean something else. The <code><b></code> tag now means <em>bring attention</em> and the <code><i></code> tag now means <em>idiomatic text</em>.</p>
<p><code><b></code> is now used to mark up keywords, product names, or other spans of text whose typical presentation would be boldfaced, but not including any special importance.</p>
<p><code><i></code> is now used to mark up text that is set off from the normal prose for readability reasons.</p>
<p>More semantic tag names have since been invented too: <code><strong></code>, <code><em></code>, or <code><mark></code>, which convey slightly different semantics.</p>
<p>If there's no semantic aspect to the piece of text you want to make bold or italic, don't use <code><b></code> or <code><i></code>, use CSS <code>font-weight</code> and <code>font-style</code> instead.</p>
</li>
<li>
<p>Misnested tags can sometimes happen in HTML, and when it does, the page doesn't break!</p>
<p>That's the beauty of HTML once again. If you're coming from an XML background, you might be surprised by the forgiveness of HTML. But, in the vast majority of cases, HTML parsers just figure things out on their own and get you what you want.</p>
<p>In our example, the markup is <code><b><i></b></i></code>, which feels obviously wrong because the closing <code></b></code> tag should appear after the closing <code></i></code> tag, to respect nesting. This particular markup creates the following DOM tree:</p>
<p><img src="https://htmhell.dev/adventcalendar/2025/8/misnested-tags-dom.png" alt="A diagram representation of the resulting DOM tree. The first node contains the text node Hello, following by a nested node, which contains the text node HTML. The node is then followed by a sibling node which contains a text node World!" /></p>
<p>This behavior is actually specified in the HTML spec, and called the <em>adoption agency algorithm</em>. I think we owe it to <a href="https://cwilso.com/">Chris Wilson</a> for thinking about this in the first place. Chris, if you ever find traces of old discussions about this, or care to write the backstory, I would be very interested!</p>
<p>Of course, I'm not saying you should do this. It's still important to create correctly nested HTML markup. But there are historical reasons for things like this to work. Back in the early days, browser engines didn't always agree on how to parse and render HTML. So, in order to ensure that as much of the web as possible was supported across all browsers, it was sometimes easier to just support how other browsers did things. And that's how things like misnested tags ended up being supported.</p>
</li>
<li>
<p>Missing end tags are fine. The HTML parser is able to close most of them on its own.</p>
<p>For example, a list item doesn't need to be closed if what follows is another list item or the end of the list. So, this works fine:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Item 1</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Item 2</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Item 3</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>The same is true for paragraphs. You can omit the closing <code></p></code> tag if what follows is another paragraph, a heading, a list, and a whole lot of other elements:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>This is a paragraph</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>This is another paragraph</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>This is a heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>This is yet another paragraph</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>You can find out more about these examples, and others, in the <a href="https://html.spec.whatwg.org/multipage/syntax.html#optional-tags">Optional tags section of the HTML spec</a>.</p>
<p>Also, think about it, you're probably already using this without realizing it. Have you ever closed a <code><img></code>, <code><input></code>, or <code><link></code> tag? Probably not, and that's fine. The HTML spec defines a whole lot of elements which don't require closing tags: <code><base></code>, <code><link></code>, <code><meta></code>, <code><hr></code>, <code><br></code>, <code><source></code>, <code><img></code>, <code><input></code>, and others.</p>
<p><a href="https://www.w3.org/TR/xhtml1/">XHTML</a> attempted to change this by requiring all tags to be closed, and conforming to XML syntax rules, but that never really caught on. Sure, HTML is weird, but it's also what powers billions of web pages today. There isn't really a reason to try and change te nature of HTML.</p>
</li>
</ol>
<h2 id="so-whats-the-moral-of-the-story?">So, what's the moral of the story?</h2>
<p>HTML can be very forgiving, and browsers implement things that may seem obscure or weird, but they do so for a very good reason: backward compatibility!</p>
<p>The web is the only platform where sites that were written years ago can still work fine today. This isn't to say that things never get removed though, they do, and probably more often than you realize. Remember AppCache, WebSQL, module import assertions, or special rules that apply to the font-size of <code><h1></code> elements when nested inside certain elements?</p>
<p>This is both a blessing and a curse. The fact that so much of the languages we use are so forgiving and time-enduring made the web what it is today: a welcoming platform that doesn't take so much effort to get used to, and kind of just works. But, this also means that old features and bad practices can linger on for a long time and, if they're used by many sites and users, can't really ever be removed.</p>
Controlling dialogs and popovers with the Invoker Commands API
2025-12-07T00:00:00Z
https://htmhell.dev/adventcalendar/2025/7/
by Aubrey Sambor<br><p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Invoker_Commands_API">Invoker Commands API</a> adds new attributes to the <code><button></code> element to control interactive elements on the page such as popovers and modal dialogs without having to write JavaScript. Dialogs have been available in all modern browsers since March 2022, and the Popover API is available in all modern browsers as of January 2025.</p>
<p>Until now, users wanting to implement the <code><dialog></code> element needed to write their own JavaScript to power the show and hide functionality using the <code>HTMLDialogElement</code> interface, while the Popover API and Invoker Commands API for popovers work identically by using <code>HTMLElement</code> attributes to show, hide, or toggle the popover.</p>
<p>Why should you use this new API, and what benefits does it bring?</p>
<h2 id="dialogs-vs-popovers">Dialogs vs Popovers</h2>
<p>First, let's go over the difference between a dialog and a popover. Hidde de Vries <a href="https://hidde.blog/dialog-modal-popover-differences/">wrote a blog post in 2022</a> detailing the differences, but in short:</p>
<ul>
<li>Dialogs usually contain an action to take, such as agreeing to text or choosing an option. Popovers usually display short-lived information such as a date picker or toast notification.</li>
<li>By default, dialog must be explicitly closed, either by taking an action within the dialog or closing via a close button. A popover can be lightly dismissed by clicking outside of the popover.</li>
<li>A dialog can either be modal or non-modal, while a popover is always non-modal by design. As of late 2025 the Invoker Commands API only has a built-in command for a modal dialog, so I will be focusing on modal dialogs only in this post.</li>
<li>Dialogs have a built in backdrop functionality to tint the background a different color, while popovers shouldn't use a backdrop in most cases.</li>
<li>A dialog is an HTML element with a role of <code>dialog</code>, while a popover is considered an attribute and needs a role to be added depending on the popover's context. See <a href="https://hidde.blog/popover-semantics/">Hidde de Vrie's post on popover semantics</a> to learn more about potential roles to add to your popover!</li>
</ul>
<h2 id="the-command-and-commandfor-attributes">The <code>command</code> and <code>commandfor</code> attributes</h2>
<p>The Invoker Commands API introduces two new attributes to the <code><button></code> elementā<code>command</code> and <code>commandfor</code>. The <code>commandfor</code> attribute acts as a connector between the <code><button></code> controlling the functionality and the element that the command acts upon, while <code>command</code> contains the action that should be taken on the element.</p>
<p>As of late 2025 the following commands are supported within the API, which right now can only be used for <code><dialog></code> or <code><div role="dialog"></code> elements or on elements with the <code>popover</code> attribute. These commands are:</p>
<ul>
<li><code>show-modal</code>: Shows a <code><dialog></code> element as a modal. If you're familiar with the underlying API to show a <code><dialog></code> as a modal, this is equivalent to the <code>HTMLDialogElement.showModal()</code> method.</li>
<li><code>close</code>: Closes a <code><dialog></code> element. This is equivalent to the <code>HTMLDialogElement.close()</code> method.</li>
<li><code>request-close</code>: This command works similarly to the <code>close</code> command, but before the <code><dialog></code> can be closed, a <code>cancel</code> event is triggered first to cancel the closure of the dialog if required actions do not occur, such as selecting required options within the <code><dialog></code>. This is equivalent to the <code>HTMLDialogElement.requestClose()</code> method. This concept is a little tricky, so I'll show an example later in the post.</li>
<li><code>show-popover</code>: Shows a popover element. This is equivalent to both the <code>popoveraction="show"</code> attribute and the <code>HTMLElement.showPopover()</code> method.</li>
<li><code>hide-popover</code>: Hides a popover element. This is equivalent to both the <code>popoveraction="hide"</code> attribute and the <code>HTMLElement.hidePopover()</code> method.</li>
<li><code>toggle-popover</code>: Toggles a popover element; if the popover is hidden, it will be shown, and if the popover is shown, it will be hidden. This is equivalent to both the <code>popoveraction="toggle"</code> attribute and the <code>HTMLElement.togglePopover()</code> method.</li>
<li>Custom values: This is where The Invoker API really shines! A user can provide a custom value, prefixed by two hyphens (<code>--</code>) to create a custom <code>command</code> event. This event is usually written in JavaScript and provided by the user to add new functionality. The possibilities are endless!</li>
</ul>
<h2 id="examples">Examples</h2>
<h3 id="the-invoker-commands-api-with-a-lessdialoggreater-element">The Invoker Commands API with a <code><dialog></code> element</h3>
<p>This is a basic example of how to use the Invoker Commands API with a <code><dialog></code> element. One button fires the <code>show-modal</code> command when it is clicked, and within the <code><dialog></code>, another button fires the <code>close</code> command to close the dialog. No JavaScript necessary, it's all built in for you!</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">commandfor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-fancy-dialog<span class="token punctuation">"</span></span> <span class="token attr-name">command</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>show-modal<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Open dialog<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</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>my-fancy-dialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Dialog content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">commandfor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-fancy-dialog<span class="token punctuation">"</span></span> <span class="token attr-name">command</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Close dialog<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dialog</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="the-request-close-command-on-a-lessdialoggreater-element">The <code>request-close</code> command on a <code><dialog></code> element</h3>
<p>To use the <code>request-close</code> command, a bit of JavaScript is needed. First, use the same code as above, but change the command on the button inside the <code><dialog></code> from <code>close</code> to <code>request-close</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">commandfor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-fancy-dialog<span class="token punctuation">"</span></span> <span class="token attr-name">command</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>show-modal<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Open dialog<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</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>my-fancy-dialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Dialog content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">commandfor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-fancy-dialog<span class="token punctuation">"</span></span> <span class="token attr-name">command</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>request-close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Close dialog<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dialog</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now, since a <code>cancel</code> event is fired on the <code><dialog></code> element (as that's the element that can be closed), an event listener needs to be added to capture the cancel event when <code>request-close</code> is called. In this example, an alert displays when the close button is clicked, and then, when the user clicks 'OK', the alert closes and then the dialog closes. This causes the user to have to perform an action before the dialog is closed.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">const</span> myFancyDialog <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"my-fancy-dialog"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">myFancyDialog<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"cancel"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> window<span class="token punctuation">.</span><span class="token function">alert</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token string">"A cancel event gets fired when using the 'request-close' command."</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<h3 id="the-invoker-commands-api-with-the-popover-attribute">The Invoker Commands API with the popover attribute</h3>
<p>Commands for the popover attribute also exist in the Invoker Commands API. The new <code>show-popover</code>, <code>hide-popover</code>, and <code>toggle-popover</code> commands are equivalent to the <code>show</code>, <code>hide</code>, and <code>toggle</code> values on the <code>popovertargetaction</code> attribute in the Popover API. The <code>popover</code> attribute still needs to be added to the popover's container, but the Invoker Commands API handles the rest of the popover's functionality.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">commandfor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>mycommandpopover<span class="token punctuation">"</span></span> <span class="token attr-name">command</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>toggle-popover<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Toggle popover<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>mycommandpopover<span class="token punctuation">"</span></span> <span class="token attr-name">popover</span><span class="token punctuation">></span></span>Popover content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="the-invoker-commands-api-with-a-custom-command">The Invoker Commands API with a custom command</h3>
<p>This is where the Invoker Commands API gets fun! You can add your own custom commands if the command you wish to run does not exist. Since a mechanism for opening and closing <code><details></code> elements does not yet exist as a command, I wrote a custom <code>--toggle-details</code> command to open and close the <code><details></code> element when the <code><button></code> element is clicked.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>my-fancy-button<span class="token punctuation">"</span></span> <span class="token attr-name">commandfor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-fancy-details<span class="token punctuation">"</span></span> <span class="token attr-name">command</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>--toggle-details<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Open details element<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</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>my-fancy-details<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>This is a details element<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Here is the text inside the details element, opened by a custom command!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">const</span> detailsElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"details"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">detailsElement<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"command"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>command <span class="token operator">===</span> <span class="token string">"--toggle-details"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> detailsElement<span class="token punctuation">.</span>open <span class="token operator">=</span> <span class="token operator">!</span>detailsElement<span class="token punctuation">.</span>open<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<h2 id="looking-ahead">Looking ahead</h2>
<p>The Invoker Commands API is not widely supported across all modern browsers just yetāas of late 2025, the Invoker Commands API is available in Chrome and Edge as of version 135, Opera as of version 120, Firefox as of version 144, and Safari Technology Preview. However, if older browsers need to be supported, you're in luck as there's a <a href="https://github.com/keithamus/invokers-polyfill">polyfill</a> created by the API author.</p>
<p>To learn what's coming next for the Invoker Commands API, the authors have created an <a href="https://open-ui.org/components/future-invokers.explainer/">explainer document</a> with potential future enhancements to the API, including commands to open, close, or toggle a <code><details></code> element, a command to open a native HTML date picker, and commands to play, pause, and mute HTML <code><audio></code> and <code><video></code> elements. Users can also <a href="https://github.com/openui/open-ui/issues">submit an issue</a> to add other commands that are not yet implementedāgo forth and add your ideas!</p>
<h2 id="further-reading">Further reading</h2>
<ul>
<li><a href="https://blog.logrocket.com/can-native-web-apis-replace-custom-components-2025/#invoker-commands">Can native web APIs replace custom components in 2025?</a></li>
<li><a href="https://developer.chrome.com/blog/command-and-commandfor">Introducing command and commandfor</a></li>
<li><a href="https://webinista.com/updates/command-and-commandfor-invoker-commands-api"><code>command</code> and <code>commandfor</code>: the Invoker Commands API</a></li>
<li><a href="https://gomakethings.com/going-javascript-free-with-the-new-invoker-command-api/">Going JavaScript-free with the new Invoker Command API!</a></li>
</ul>
Accessible by Design: The Role of the 'lang' Attribute
2025-12-06T00:00:00Z
https://htmhell.dev/adventcalendar/2025/6/
by Todd Libby<br><p>When starting a project, whether it is an application, a mobile app or site, or just a website in general I still see an alarming number of examples where the language attribute is not included in the <code><html></code> element. Not the <code>!DOCTYPE</code>, but the element directly after the DOCTYPE.</p>
<p>I have audited many sites and many frameworks in the past, I have noticed an alarming omission right from the outset when developers are building sites or applications. Especially in the mobile space and let's face it, in web development we focus on making things for ourselves and if <em>it works on our computer, it must work everywhere! Right?</em></p>
<p>I see it more prevalent these days. There are surveys out and the issue of accessibility education in university or boot camps still lacks. New developers entering the field who aren't aware, framework authors that just don't know, understand, or they just don'make their work accessible.</p>
<p>I am here to discuss the importance of the language attribute in your code.</p>
<h2 id="the-attribute-and-the-importance-of-the-language-used">The Attribute and the Importance of the Language Used</h2>
<p><strong>Sometimes, a tiny detail can make or break the experience for millions of users.</strong> One of these tiny, powerful details is the <code>lang</code> attribute in your HTML.</p>
<p>The <code>lang</code> attribute is a simple piece of code that tells <em>web browsers and screen readers</em> what human language your page is written in. For example:</p>
<p><code><html lang="en"></code> means the page is in English.<br />
<code><html lang="es"></code> means the page is in Spanish.</p>
<p>When you forget this attribute, you're not just missing a semantic tagāyou're creating a <strong>major accessibility barrier</strong>. If you don't tell the computer what language you're using, assistive tools won't know how to read your content correctly.</p>
<h2 id="there-is-data-here-and-you-should-read-it">There Is Data Here and You Should Read It</h2>
<p>The <a href="https://webaim.org/projects/million/">WebAIM Million Report</a> is an accessibility report done by WebAIM every year and it's an accessibility evaluation of the top one million homepages on the internet. 2025 marked the seventh year this has been done and the results are not surprising.</p>
<h3 id="lets-show-the-data-for-the-language-attribute">Let's show the data for the language attribute.</h3>
<img src="https://res.cloudinary.com/colabottles/image/upload/v1760556179/images/webaim-million-2.png" alt="A graph showing the top six accessibility issues found in the top one million websites by WebAIM. Low contrast of text is number one followed by missing alt text, missing labels, empty links, empty buttons and finally missing language attribute." width="702" height="262" loading="lazy" />
<p>For the seventh year in a row, a missing document language made the list.</p>
<img src="https://res.cloudinary.com/colabottles/image/upload/v1760556179/images/webaim-million-3.png" alt="A graph showing the top six accessibility issues found in the top one million websites by WebAIM by year starting in 2019 up to 2025. Low contrast of text is number one followed by missing alt text, missing labels, empty links, empty buttons and finally missing language attribute." width="702" height="297" loading="lazy" />
<p>As with the rest of the items in the data, it has been a common theme the last seven years. Missing language attribute has always been the last item on the <em>repeating list of common failures</em>. So what are the implications?</p>
<p>A numerical look shows the data is still trending to the <strong>same six problems</strong> in the report. So why is it that these issues are the ones that stay in the top six?</p>
<figure>
<img src="https://res.cloudinary.com/colabottles/image/upload/v1760556179/images/webaim-million-1.png" alt="The WebAIM Million report showing the percentage of top million websites tested and the percentage of those with issues." width="702" height="276" loading="lazy" />
<figcaption>The WebAIM Million Report showing low contrast of text at 79.1% followed by missing alternative text for images at 55.5%, missing form input labels at 48.2%, empty links at 45.4%, empty buttons at 29.6%, and finally missing language attribute at 15.8%.</figcaption>
</figure>
<h2 id="what-happens-when-the-language-is-missing?-the-wrong-voice-problem">What Happens When the Language is Missing? The Wrong Voice Problem</h2>
<p>The main group affected by a missing <code>lang</code> tag is the screen reader user. Screen readers are essential tools that read web content aloud. They're mainly used by people who are blind, have low vision or for those that use text-to-speech. They are also used by people that find reading difficult for other reasons, this is a common practice with people with ADHD (Adult attention-Deficit/Hyperactivity Disorder).</p>
<p>Screen readers don't just use one voice; they use specialized software packages for each language. This software knows the pronunciation rules, rhythm, and stress for English, French, Japanese, etc.</p>
<p>When your page is missing the <code>lang</code> attribute, the screen reader has to guess the language. It usually guesses based on the user's computer settings (for example, if the user lives in Germany, the screen reader will try to use the German voice).</p>
<h3 id="example-english-text-read-by-a-german-voice">Example: English Text Read by a German Voice</h3>
<p>Imagine your entire website is in clear English. If a German screen reader tries to read it, it will apply German pronunciation rules.</p>
<p class="highlight"><strong>“The” might sound like “Tee-hay.”</strong></p>
<p>or;</p>
<p class="highlight"><strong>“Data” might be pronounced with a hard ‘A’ sound instead of a soft one.</strong></p>
<p>The result is garbled, unnatural, and often unintelligible speech. The text is still on the page, but for the screen reader user, the content is lost. They cannot understand your article, buy your product, or use your service.</p>
<p>This single small mistake transforms your helpful website into a frustrating, unusable experience.</p>
<h2 id="its-a-rule-not-a-suggestion-(wcag)">It's a Rule, Not a Suggestion (WCAG)</h2>
<p>Using the <code>lang</code> attribute isn't just a friendly suggestion; it's a <strong>core requirement</strong> for making your website accessible.</p>
<p>The <em><a href="https://www.w3.org/TR/WCAG22/" title="The Web Content Accessibility Guidelines">Web Content Accessibility Guidelines (WCAG)</a></em> are the international standard for web accessibility. WCAG <strong><a href="https://www.w3.org/WAI/WCAG22/Understanding/language-of-page.html" title="Success Criteria 3.1.1 - Language of Page">Success Criterion 3.1.1 (Language of Page)</a></strong> states that the language of the page must be clear to the computer. This is a level āAā requirement, which means it's mandatory for basic accessibility.</p>
<p>If your website fails this check, it is officially considered inaccessible.</p>
<h2 id="how-it-affects-other-tools">How It Affects Other Tools</h2>
<p>The <code>lang</code> attribute helps more than just screen readers:</p>
<h3 id="1-braille-displays">1. Braille Displays</h3>
<p>A refreshable braille display translates text into small patterns of raised bumps. Different languages use different contraction rules in braille (called Grade 2 braille). If the language is not set, the braille translator might use the wrong rules, turning clear text into meaningless gibberish for the braille reader.</p>
<h3 id="2-automated-translation">2. Automated Translation</h3>
<p>When a user relies on tools like Google Translate or a browser's built-in translation feature, telling the tool the source language (the language you wrote it in) ensures a much more accurate translation. If the source language is unclear, the translation quality drops sharply. <a href="https://www.matuzo.at/blog/lang-attribute/">An example can be found here</a>.</p>
<h3 id="3-quotation-marks">3. Quotation Marks</h3>
<p>The <code>lang</code> attribute helps the browser and other user agents select the correct typographical glyphs for quotation marks, especially when it comes to when the <code><q></code> and <code><blockquote></code> elements are used (when styled using CSS generated content such as <code>content: open-quote</code>). For example:</p>
<ul>
<li>
<p>In English <code>lang="en"</code>, quotes are typically ādouble quotesā.</p>
</li>
<li>
<p>In German <code>lang="de"</code>, they are often rendered as ālow-9 quotesā.</p>
</li>
<li>
<p>In French <code>lang="fr"</code>, they use Ā« guillemets Ā».</p>
</li>
</ul>
<p>While less related to <em>visual</em> quotation marks, providing the correct language helps assistive technologies <strong>pronounce</strong> the surrounding text accurately, ensuring a fluid and comprehensible reading experience.</p>
<p>Not providing the correct language may cause browsers to default to the user's system language or a neutral setting for quotation marks which may not match the document's language which results in incorrect or confusing typography (e.g., using English quote marks for German language).</p>
<p>Without a declared language, a screen reader may attempt to read the text using incorrect phonetic rules, voice, and accent. Which makes the content sound like gibberish and can make it incomprehensible for users who rely on audio output.</p>
<h3 id="4-hyphenation">4. Hyphenation</h3>
<p>Proper hyphenation is entirely language-dependent. Hyphenation rules can be complex and unique to each language. when CSS is used, <code>hyphens: auto</code>, the browser or user agent relies on the <code>lang</code> attribute to load the appropriate hyphenation dictionary and apply correct linguistic rules which can improve text flow and readability. Especially in justified or narrow columns.</p>
<p>For example, a long compound word in German, <code>lang="de"</code>, will be broken according to German rules such as <strong>Rechtsschutzversicherungsgesellschaften</strong> (which means, insurance companies providing legal protection).</p>
<p>Most browsers do not provide automatic hyphenation if the language is not declared. This can not only lead to unsightly text blocks with excessive white space between words, but also horizontal scrolling or overflow on mobile devices which severely impacts readability and layout stability.</p>
<p>If the browser attempts to guess the language or uses the wrong default, it could apply the incorrect hyphenation rules, which breaks words in places that are linguistically wrong, which, in turn, confuses the reader.</p>
<h2 id="what-about-pages-with-two-languages?">What About Pages with Two Languages?</h2>
<p><em>What if your page is mostly English but includes a quote in Spanish?</em> If you don't do anything, the screen reader will read the Spanish quote using the English voice, again leading to mispronunciation.</p>
<p>You can fix this instantly by adding the <code>lang</code> attribute to the specific element that changes language:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> The artist once said, "Always remember this phrase:</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fr<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Je ne regrette rien.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>" I think that sums up his career.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>In this code, the screen reader switches to the French voice for the quote and then immediately switches back to the English voice for the rest of the sentence. This small change ensures all users hear the content exactly as intended.</p>
<h2 id="how-to-set-the-language-in-modern-web-frameworks">How to Set the Language in Modern Web Frameworks</h2>
<p>In modern websites built with tools like React, Vue, or Angular, you usually don't touch the main HTML file very often. Since these tools mostly control the content inside the <code><body></code> tag, you have to know where to find the root template file to set the <code>lang</code> attribute correctly. for example,</p>
<p>React uses the file, <code>public/index.html</code>. Therefore you would directly place the attribute in the <code><html></code> tag in that file.</p>
<h3 id="heres-a-simple-guide-for-the-most-popular-frameworks">Here's a simple guide for the most popular frameworks:</h3>
<table>
<thead>
<tr>
<th style="text-align:left">Framework</th>
<th style="text-align:center">What File to Edit</th>
<th style="text-align:right">Where to Put the Code</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">React</td>
<td style="text-align:center">public/index.html</td>
<td style="text-align:right">Directly on the <code><html></code> tag in that file.</td>
</tr>
<tr>
<td style="text-align:left">Next.js</td>
<td style="text-align:center">app/layout.tsx (or similar root file)</td>
<td style="text-align:right">Set the lang in the JSX for the root <code><html></code> element.</td>
</tr>
<tr>
<td style="text-align:left">Vue</td>
<td style="text-align:center">public/index.html</td>
<td style="text-align:right">Directly on the <code><html></code> tag in that file.</td>
</tr>
<tr>
<td style="text-align:left">Nuxt</td>
<td style="text-align:center">nuxt.config.ts</td>
<td style="text-align:right">Inside the app.head.htmlAttrs setting in your config file.</td>
</tr>
<tr>
<td style="text-align:left">Angular</td>
<td style="text-align:center">src/index.html</td>
<td style="text-align:right">Directly on the <code><html></code> tag in that file.</td>
</tr>
<tr>
<td style="text-align:left">Svelte/SvelteKit</td>
<td style="text-align:center">index.html or src/app.html</td>
<td style="text-align:right">Directly on the <code><html></code> tag in the main template file.</td>
</tr>
</tbody>
</table>
<h3 id="example-setting-the-language-in-a-static-template">Example: Setting the Language in a Static Template</h3>
<p>For most simple apps (React, Angular, plain HTML), you will open your main <code>index.html</code> file and change the first line like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token comment"><!-- Change the line below from <html> to the correct language code --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Your app code loads here --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>The <code>lang</code> attribute is a tiny line of code that provides <strong>universal access to your content</strong>. It's arguably the easiest, fastest, and most impactful accessibility fix you can make on any website.</p>
<p>By correctly setting the language, you ensure that everyone has equal access to your content. Regardless of whether they use a screen reader, braille display, or translation tool to do so, their tools have the fundamental information they need to do their jobs correctly. It's a simple commitment that makes the web better for everyone.</p>
<p>Don't let a missing two-letter code turn your content into a foreign language for your users and don't be afraid to use it or add it in!</p>
IDREFs: What they are and how to use them
2025-12-05T00:00:00Z
https://htmhell.dev/adventcalendar/2025/5/
by Kilian Valkhof<br><p>Take the following HTML for example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Email address</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p>Here, the <code><label></code> element is associated with the <code><input></code> element because the input is nested inside of the label, and the browser now knows to offer additional behavior: when the user clicks on the label, the input will be focused. That's not behavior that you had to specify with an onclick handler, the browser just infers it from the relationship between the elements.</p>
<p>But what if it can't infer the relationship? While your Firefox, Chrome or <a href="https://polypane.app/">Polypane browser</a> can all figure this out, some assistive technologies can't. Both Dragon Naturally Speaking and VoiceOver on macOS have trouble associating labels with inputs when they are nested, according to <a href="https://a11ysupport.io/tests/html_label_element_implicit">a11ysupport tests</a>. So while clicking the label will focus the input, giving the voice command "Focus Email address" might not work. That said, support might have improved since the tests were last conducted: My editor Zoƫ tested this in Safari 26.0.1 on macOS 15.7.1 where it now works. Make sure to test before relying on this behavior!</p>
<p>Additionally, this association only works when the input is nested inside the label. If you wanted to have the label and input be siblings, for example for styling purposes, then that implicit association is lost.</p>
<p>To solve this, we can use the <code>IDREF</code> you probably already know, the <code>for</code> attribute on the <code><label></code> element:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Email address <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<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>email-input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>Now we're <em>explicitly</em> associating the label with the input, by referencing the input's <code>id</code> from the label's <code>for</code> attribute. That's all an <code>IDREF</code> really is: a reference to another element's <code>id</code>.</p>
<p>Note, there's also nothing keeping you from using the <code>for</code> attribute and nesting both at the same time:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Email address</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<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>email-input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now you know what an <code>IDREF</code> is! In HTML there are many other places where you can use <code>IDREF</code>s to create explicit relationships between elements, many of which are ARIA attributes that help with accessibility and with describing more complex relationships than HTML can express. We'll get to those in a bit, because there's more to be said about <code>IDREF</code>s themselves.</p>
<h2 id="ids-need-to-be-unique">IDs need to be unique</h2>
<p>In a well-structured HTML document, each <code>id</code> is only used once. This is important for <code>IDREF</code>s to work properly, since the browser or assistive technology needs to be able to find the single right element when following an <code>IDREF</code>. If there are multiple elements with the same <code>id</code>, this can lead to unexpected behavior.</p>
<p>Making sure IDs are unique can be tricky: you might have multiple forms on a page that reuse the same naming, or another repeated structure that requires ARIA attributes. When that happens, consider a programmatic way to generate unique IDs, for example by prefixing them with the component name.</p>
<h2 id="ids-need-to-exist">IDs need to exist</h2>
<p>This might seem obvious, but the majority of issues that occur around <code>IDREF</code>s is that the referenced ID simply doesn't exist in the document. This can happen when you make a typo in the <code>id</code> or <code>IDREF</code>, or when you remove an element but forget to update the references to it.</p>
<p>Linters and validators can help catch these issues, as can browser developer tools that highlight broken references. In Polypane, for example, broken <code>IDREF</code>s are <a href="https://polypane.app/docs/elements-panel/#idrefs">highlighted in the Element panel</a>, making it easy to spot and fix them.</p>
<p><img src="https://htmhell.dev/images/advent2025/idrefs/idrefs.png" alt="Polypane's Element panel highlighting a broken for attribute" /></p>
<p>In other browsers you can check for missing IDs using this little console snippet that gathers all <code>for</code> attributes on labels and checks if the referenced ID exists:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"[for]"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">label</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> id <span class="token operator">=</span> label<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"for"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Label with for="</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">" has no matching element with that ID.</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> label</span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<h2 id="other-html-idref-attributes">Other HTML IDREF attributes</h2>
<p>HTML has quite a few attributes that use <code>IDREF</code>s to create relationships between elements, including some <em>very</em> new ones.</p>
<h3 id="the-for-attribute-on-lesslabelgreater-elements">The <code>for</code> attribute on <code><label></code> elements</h3>
<p>As we've seen above.</p>
<p>A neat trick that <code>for</code> has: you can link as many labels as you want to a single input:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Email address <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<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>email-input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> required <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p>These will all focus the same input, and their combinined text will be used as the accessible name for the input: <code>Email address required</code>. Of course, that's the <em>theory</em>. In practice, support for inputs with multiple labels is inconsistent. Some assistive technologies will use all labels but some only use the first or the last one (see <a href="https://github.com/dequelabs/axe-core/issues/689#issuecomment-490176712"> this research</a> done by Deque). So while it's valid HTML, you're better off sticking to a single label per input for now.</p>
<h3 id="the-form-attribute-on-form-associated-elements">The <code>form</code> attribute on form-associated elements</h3>
<p>You can add a <code>form</code> attribute to form-associated elements like <code><input></code>, <code><button></code>, <code><select></code>, <code><fieldset></code> and <code><textarea></code> to associate them with a specific <code><form></code> element on your page, even if they are not nested inside that form:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</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>signup-form<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">form</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>signup-form<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sign up<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Something to keep in mind is that the <code>form</code> atttribute <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/form#non-inheritance_of_the_form_attribute">only works for the current element</a>, not its children. If you have a <code><fieldset></code> with a <code>form</code> attribute, the inputs inside it will not be associated with the form unless they also have a <code>form</code> attribute.</p>
<p>Another thing to keep in mind is that you can remove an element from a form by giving it a <code>form</code> attribute that points to a different form, or to no form at all (by giving it an empty string). Here's <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/form#associating_with_a_non-ancestor_form">an example from MDN</a>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</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>externalForm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</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>internalForm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">form</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>externalForm<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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<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>username<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>Even though the label will focus that input, the input will be submitted with <code>externalForm</code> and not for <code>internalForm</code>.</p>
<p>Like the advice for labels, keeping form-associated elements inside their forms is generally easier to manage. The above might be useful design-wise when you have a wizard across multiple steps that each have their own form, but you want a submit button that's always visible across the steps.</p>
<h3 id="the-list-attribute-on-lessinputgreater-elements">The <code>list</code> attribute on <code><input></code> elements</h3>
<p>If you have an <code><input></code> element, you can use the <code>list</code> attribute to associate it with a <code><datalist></code> element that contains predefined options for that input to have it show a dropdown and autocomplete with those options:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">list</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>browsers<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>browser-choice<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>browser-choice<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>datalist</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>browsers<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Chrome<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Firefox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Safari<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Polypane<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>datalist</span><span class="token punctuation">></span></span></span></code></pre>
<p>This works on desktop browsers, but you should not depend on it: mobile browsers often don't show the datalist options at all, and assistive technologies may not announce them either. So while it's a nice enhancement, make sure your form works well without it too.</p>
<h3 id="the-headers-attribute-on-lesstdgreater-and-lessthgreater-elements">The <code>headers</code> attribute on <code><td></code> and <code><th></code> elements</h3>
<p>When you have complex tables where header cells might have subheaders, you can use the <code>headers</code> attribute on data cells (<code><td></code>) and header cells (<code><th></code>) to explicitly associate them with the relevant header cells:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</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">colspan</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</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>first<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</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>last<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Last<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">headers</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>first name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Kilian<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">headers</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>last name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Valkhof<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></span></code></pre>
<p>And here we also introduce the concept of multiple <code>IDREF</code>s in a single attribute. Some <code>IDREF</code> attributes can reference <em>multiple</em> IDs by separating them with spaces. In this case, the first data cell is associated with both the "First" and "Name" headers. When browsers now announce the cell, they can include the text of both headers in the announcement.</p>
<h3 id="new-html-idref-attributes-popovertarget-commandfor-anchor-and-interestfor">New HTML <code>IDREF</code> attributes: <code>popovertarget</code>, <code>commandfor</code>, <code>anchor</code> and <code>interestfor</code></h3>
<p>HTML is in the process of getting several new functionalities that make it easier to create declarative interactive components like popups and tooltips. To help with those, new <code>IDREF</code> attributes are being added.</p>
<p><code>popovertarget</code> can be added to a button to associate it with a popup element that has the <code>popover</code> attribute. That way the popover is shown when the button is activated. For a more in-depth explanation, check out <a href="https://www.htmhell.dev/adventcalendar/2024/22/">PSA: Stop using the title attribute as tooltip!</a> from last year's advent calendar.</p>
<p>The default action for <code>popovertarget</code> is to toggle the popover when the button is activated, but you can use the <code>popovertargetaction</code> attribute to change that behavior. The default value is <code>toggle</code>, but you can also set it to <code>show</code> or <code>hide</code> to always show or hide the popover when the button is activated.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">popovertarget</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-popover<span class="token punctuation">"</span></span> <span class="token attr-name">popovertargetaction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>show<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Show popover</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>my-popover<span class="token punctuation">"</span></span> <span class="token attr-name">popover</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> This is a popover!</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">popovertarget</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-popover<span class="token punctuation">"</span></span> <span class="token attr-name">popovertargetaction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>hide<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Close<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p><code>commandfor</code> does the same as popovertarget, but can also be used to open and close <code><dialog></code> elements declaratively with <code>show-modal</code> and <code>close</code> as <code>command</code> values.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">commandfor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-dialog<span class="token punctuation">"</span></span> <span class="token attr-name">command</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>show-modal<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Open dialog<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</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>my-dialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> This is a dialog!</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">commandfor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-dialog<span class="token punctuation">"</span></span> <span class="token attr-name">command</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Close<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dialog</span><span class="token punctuation">></span></span></span></code></pre>
<p>Lastly, <code>interestfor</code> is similar to <code>commandfor</code>, but is used when a user "expresses interest" in an element, for example by hovering over it or focusing it. The use case here is to show tooltips or hint popovers when the user hovers or focuses an element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">interestfor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-tooltip<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I'm a button!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>my-tooltip<span class="token punctuation">"</span></span> <span class="token attr-name">popover</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>hint<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>The button does nothing.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>For more on <code>interestfor</code>, check out the OpenUI explainer on <a href="https://open-ui.org/components/interest-invokers.explainer/">interest invokers</a>.</p>
<p><code>popovertarget</code> works in all browsers, while <code>commandfor</code> is still lacking support in Safari. <code>interestfor</code> is still very new and only supported in Chromium 142 and newer.</p>
<p><em>To learn more about these, keep an eye on upcoming articles in this advent calendar!</em></p>
<p>Lastly, the <code>anchor</code> attribute will let you declaratively specify which element a popover or tooltip should be anchored to. Currently this is something you have to specify with CSS, but it can be much easier to reference an ID instead. Unfortunately <a href="https://caniuse.com/mdn-html_global_attributes_anchor">it's not supported yet</a>.</p>
<h2 id="aria-idref-attributes">ARIA IDREF attributes</h2>
<p>All of which brings us to ARIA. ARIA is a set of attributes designed to add semantics, relationships and behaviors to HTML where the native elements and attributes fall short. They're not the first thing you should reach for, but there are many things where HTML alone can't express what you need.</p>
<p>Many ARIA attributes are <code>IDREF</code> attributes that allow you to create explicit relationships between elements.</p>
<h3 id="descriptive-relationships">Descriptive relationships</h3>
<p>A common use case for ARIA is to make sure that elements have an accessible name and/or description. Often you'll see this done with <code>aria-label</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Move to trash<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<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>icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>š<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>This works, but aria-label comes with some downsides. For example, it's easy to miss: a common issue accessibility auditors find is that a button's HTML is copied for another purpose, but the original aria-label is not updated to reflect the new purpose. Browsers also have issues with automatic translations of aria-labels, since they are not part of the visible text on the page.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Move to trash<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- oops! --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>š Revert<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>It is often better to use <code>aria-labelledby</code> to give an element its accessible name. <code>aria-labelledby</code> gives elements an accessible name by referencing other elements that contain the relevant text. Indeed, <code>aria-labelledby</code> can reference multiple IDs, allowing you to combine text from different elements into a single accessible name:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>photo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>trash-label photo-label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<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>icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>š<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>trash-label<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>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Delete<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>photo-label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>IMG_0512.jpg<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>IMG_0512.jpg<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>Sydney Opera House at sunset<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>The above button's accessible name will be "Delete IMG_0512.jpg", combining the text from both referenced elements and making it clear that the button deletes, and which photo it deletes.</p>
<p>Sometimes elements might have an accessible name, but you still want to have an additional description to give more info. Visually that is easy to do, think of the word "required" below an <code><input></code> element. That association is not always clear to assistive technologies. To make that association explicit, you can use <code>aria-describedby</code> and reference IDs of elements that contains the description.</p>
<p>Again, <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-describedby">MDN</a> gives us a nice example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>trash-desc<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Move to trash<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>trash-desc<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Items in the trash will be permanently removed after 30 days.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>The button's accessible name "Move to trash" is understandable on its own, but the description provides additional context about what happens when you move something to the trash.</p>
<h3 id="complex-relationships">Complex relationships</h3>
<p>ARIA also has many attributes that help express more complex relationships between elements: <code>aria-controls</code>, <code>aria-owns</code>, <code>aria-activedescendant</code> and <code>aria-flowto</code>.</p>
<p><code>aria-controls</code> is used to indicate that an element controls another element, for example a button that shows or hides a section of content. This can help assistive technologies understand the relationship between the button and the content it controls:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>submenu<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expaned</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>Open menu<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>submenu<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<p>Here, the button is indicating that it controls the visibility of the <code>nav</code> element with the ID <code>submenu</code>. You can then combine this with <code>aria-expanded</code> on the button to indicate whether the submenu is currently visible or hidden.</p>
<p>To account for even more complex structures, <code>aria-controls</code> can also reference multiple IDs, allowing a single control to manage several elements at once.</p>
<p><code>aria-activedescendant</code> is used to indicate which element within a <em>composite widget</em> is currently active. An example of a composite widget is a combobox: a UI component that combines a text input with a list of options: You can type in the text input to filter the options, fill in freeform content or use the arrow keys to navigate the options. For a reference implementation look at the <a href="https://www.w3.org/WAI/ARIA/apg/patterns/combobox/">combobox</a> in the WAI-ARIA Authoring Practices. Getting a combobox right is tricky, so make sure to read that guide if you're building one!</p>
<p>You can use <code>aria-activedescendant</code> (in combination with <code>aria-controls</code>) on the input to indicate which option in the listbox is currently selected:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span><br /><span class="highlight-line"> <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><br /><span class="highlight-line"> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>combobox<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">aria-autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">aria-haspopup</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><br /><span class="highlight-line"> <span class="token attr-name">aria-expanded</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><br /><span class="highlight-line"> <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>options-list<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <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>option-2<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</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>options-list<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>listbox<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>options<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</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>option-1<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>option<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Option 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</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>option-2<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>option<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Option 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</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>option-3<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>option<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Option 3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p><code>aria-owns</code> is used to create a parent-child relationship between elements that are not nested in the DOM. When for some reason your DOM can't be structured in a way that makes sense visually or semantically (for example, you need to use a "portal" in some Javascript frameworks),<code>aria-owns</code> will let you "rewrite" the accessibility tree as if the elements were nested. Any elements that are inside the element are listed first, and then the elements you refernece in <code>aria-owns</code> are added as children after that.</p>
<p>Keep in mind that when you use <code>aria-owns</code>, all the regular rules about HTML still apply: You can't nest an interactive element inside another interactive element, and some elements can only have specific types of children. Because of this, giving a specific example for <code>aria-owns</code> will always feel a little contrived. I'm going to try anyway.</p>
<p>Here's how you would structure a combobox where the list of options needs to be elsewhere in the DOM, for example to break out of a container with <code>overflow:hidden</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>combobox-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Choose an option:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-owns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>floating-list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span><br /><span class="highlight-line"> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>combobox-input<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <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><br /><span class="highlight-line"> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>combobox<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">aria-autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">aria-haspopup</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><br /><span class="highlight-line"> <span class="token attr-name">aria-expanded</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><br /><span class="highlight-line"> <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>floating-list<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <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><span class="token punctuation">"</span></span></span><br /> <span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- elsewhere in the dom to avoid stacking context clipping --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</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>floating-list<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>listbox<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>options<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>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>option-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Option 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>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>option-2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Option 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>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>option-3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Option 3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>Note that we use both <code>aria-controls</code> and <code>aria-owns</code> here. <code>aria-controls</code> indicates that the div controls the listbox, while <code>aria-owns</code> indicates that the listbox should be a child of the div in the accessibility tree. <code>aria-owns</code> describes a <em>structural</em> relationship, while <code>aria-controls</code> describes a <em>functional</em> relationship.</p>
<p><code>aria-owns</code> on its own doesn't change the browser's default behavior, where the tab order follows the DOM structure. Assistive technologies can instead use the <code>aria-flowto</code> relationships to offer the user a way to navigate content in the suggested order.</p>
<p>In other words, <code>aria-flowto</code> is used to indicate a logical reading order between elements that doesn't follow the visual ordering of elements. That can happen when they're in different places in the DOM, or when you've changed the visual order with the <code>order</code> CSS property, or by absolute positioning elements. Using <code>aria-flowto</code> can help assistive technologies navigate the content in a way that makes sense.</p>
<p><code>aria-flowto</code> can also reference multiple IDs. In that case, the assistive technology can give the user a choice of which element to navigate to next.</p>
<p>As with all things ARIA, <code>aria-flowto</code> is a last resort: it's better to structure your HTML in a way that follows a logical reading order without needing to override it with ARIA. This is doubly true for <code>aria-flowto</code> because <a href="https://a11ysupport.io/tech/aria/aria-flowto_attribute">support in browsers and assistive technologies is limited</a>: only the JAWS screen reader supports it at the moment.</p>
<h3 id="do-you-need-aria-idrefs?">Do you need ARIA IDREFs?</h3>
<p>As you can see from the descriptions above, the ARIA attributes get increasingly more esoteric and complex. Things like <code>aria-labelledby</code> and <code>aria-controls</code> can be useful in many situations, but others like <code>aria-owns</code> and <code>aria-flowto</code> are only needed for specific use cases. When you think you need ARIA it's often better to take a step back and consider if you can achieve your goal with native HTML elements and attributes.</p>
<p>It's also important to note that none of these ARIA attributes bring any behavior on their own: they only describe relationships. This can help assistive technologies understand the structure of your page, but you'll still have to implement the actual behavior. For example, <code><button aria-expanded="false" aria-controls="my-menu"></code> will not automatically show and hide that menu element or move the focus into it. You have to implement the show/hide behavior and focus management. The ARIA attributes just tell the browser that this button controls that menu and whether it's expanded or not.</p>
<h2 id="idrefs-and-how-to-use-them">IDREFs and how to use them</h2>
<p><code>IDREF</code>s let you create explicit relationships between elements in your HTML where they otherwise might not exist. Some of them give you additional behaviors, like how the <code>for</code> attribute on a label makes clicking the label focus the input, or how the <code>list</code> attribute on an input shows a dropdown of options from a datalist.</p>
<p>Other <code>IDREF</code> attributes, especially in ARIA, are used to describe relationships that help assistive technologies understand the structure and purpose of your content better. If you also want those to show certain behaviors, it's your job to implement those.</p>
<p>Whenever you use <code>IDREF</code>s, make sure that the referenced IDs exists, and that you test their usage with the browsers and assistive technologies your users use.</p>
Referencing HTML elements inside Shadow DOM
2025-12-04T00:00:00Z
https://htmhell.dev/adventcalendar/2025/4/
by mehm8128<br><p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components">Web Components</a> is the web standard way for creating reusable components like React or Vue components.</p>
<p>While Web Components have matured significantly, it still has some missing pieces to make accessible components easily using <a href="https://www.matuzo.at/blog/2023/pros-and-cons-of-shadow-dom/">Shadow DOM</a>.<br />
Today I'll introduce one of these difficulties and the proposed solution to resolve it. Part of this solution is already available in Chrome Canary.</p>
<h2 id="problems-with-referencing-html-elements-inside-the-shadow-dom">Problems with referencing HTML elements inside the Shadow DOM</h2>
<p>First, let's consider a case where we create a checkbox component using custom elements and <a href="https://web.dev/articles/declarative-shadow-dom">Declarative Shadow DOM</a>.<br />
In this example, we want to reference the internal <code><input></code> element by referencing <code>id="checkbox"</code> on <code><fancy-checkbox></code>. Elements inside the Shadow DOM are encapsulated, that's why we cannot reference the input field directly.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"> customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token string">"fancy-checkbox"</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token keyword">class</span> <span class="token class-name">FancyCheckbox</span> <span class="token keyword">extends</span> <span class="token class-name">HTMLElement</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>checkbox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I agree with the terms and conditions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fancy-checkbox</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>checkbox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name">shadowrootmode</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>inner-checkbox<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fancy-checkbox</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>The problem with this approach is that the <code><input type="checkbox"></code> is failing to be associated with the <code><label></code>:</p>
<ol>
<li>Clicking the label doesn't focus on the checkbox.</li>
<li>The checkbox doesn't have an accessible name.</li>
</ol>
<p>This is the missing piece that this article addresses. It is not easy to make Web Components fully accessible using Shadow DOM.</p>
<p>I'll introduce the proposed solutions to address these problems.</p>
<h2 id="solutions">Solutions</h2>
<p><a href="https://github.com/WICG/webcomponents/blob/gh-pages/proposals/reference-target-explainer.md">The proposal</a> divides the work into two phases to simplify the problems.<br />
Phase 1 only deals with targeting a single element in Shadow DOM.<br />
Phase 2 addresses targeting multiple elements.</p>
<p>For the initial release, only Phase 1 will be shipped.</p>
<h3 id="phase-1">Phase 1</h3>
<p>Let's consider an "enclosing element" concept. An "enclosing element" wraps another element and extends it with extra features and HTML elements.<br />
For example, when we create a <code><fancy-checkbox></code> component using the Shadow DOM, this becomes the "enclosing element" for <code><input type="checkbox"></code>. This allows us to encapsulate and make reusable components with custom styling and additional functionality.</p>
<p>To enable referencing the <code><input type="checkbox"></code> element inside the Shadow DOM from <code><label></code>, Phase 1 introduces the <code>shadowRootReferenceTarget</code> attribute. This attribute specifies which element should be referenced as the target of <code><label for="checkbox"></code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>checkbox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I agree with the terms and conditions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fancy-checkbox</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>checkbox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name">shadowrootmode</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open<span class="token punctuation">"</span></span> <span class="token attr-name">shadowRootReferenceTarget</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>inner-checkbox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>inner-checkbox<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fancy-checkbox</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now, when <code><label for="checkbox"></code> tries to reference <code><fancy-checkbox id="checkbox"></code>, it automatically references <code><input id="inner-checkbox"</code> because <code>shadowRootReferenceTarget</code> attribute designates its mapping. Finally, the label can reference the checkbox and provide the accessible name "I agree with the terms and conditions" for it. Users can also focus on it by clicking <code><label for="checkbox"></code>.</p>
<p>This also enables the use of ARIA attributes, as shown in the following example using <code>aria-labelledby</code>. The same applies to <code>popovertarget</code>, <code>commandfor</code>, and <code>interestfor</code>.<br />
What attributes are in <a href="https://github.com/WICG/webcomponents/issues/1091">scope is not completely decided</a>. Whether <code>aria-owns</code> should be included will be discussed and all other IDREF attributes are likely to be in scope.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>label<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fancy-label</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>label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name">shadowrootmode</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open<span class="token punctuation">"</span></span> <span class="token attr-name">shadowRootReferenceTarget</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>inner-label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</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>inner-label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Type your name.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fancy-label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Phase 1 is available in <a href="https://wpt.fyi/results/shadow-dom/reference-target/tentative?label=master&label=experimental&aligned">Chrome Canary</a> with the "Experimental Web Platform features" flag enabled.</p>
<p>Mozilla set their position to "positive" in <a href="https://github.com/mozilla/standards-positions/issues/1035">September</a>, and this feature is proposed as part of <a href="https://github.com/web-platform-tests/interop/issues/1011">Interop 2026</a>. I hope this will become available across all major browsers.</p>
<p>Let's take a look at Phase 2 for multiple elements reference support.</p>
<h3 id="phase-2">Phase 2</h3>
<p><a href="https://github.com/WICG/webcomponents/blob/gh-pages/proposals/reference-target-explainer.md#-phase-2-referring-to-specific-elements-within-a-shadow-root">Phase 2</a> enables referencing multiple elements or other complicated references.<br />
As one of the solution for phase 2, I introduce <code>shadowRootReferenceTargetMap</code> attribute, but other solutions are discussed so it's unclear what solution will be adopted for complicated reference.</p>
<p>Let's consider creating a combobox using <code>aria-controls</code> and <code>aria-activedescendant</code>.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span><br /><span class="highlight-line"> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>combobox<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <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><br /><span class="highlight-line"> <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>animals<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <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>animals<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>animals-listbox</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>animals<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><br /><span class="highlight-line"> <span class="token attr-name">shadowrootmode</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">shadowRootReferenceTargetMap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>aria-controls: listbox,<br /> aria-activedescendant: opt1<span class="token punctuation">"</span></span><br /> <span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>animals-listbox</span><span class="token punctuation">></span></span></span></code></pre>
<p>The first part of the value of the <code>shadowRootReferenceTargetMap</code> attribute, <code>aria-controls: listbox</code>, means when <code>aria-controls</code> references <code><animals-listbox></code> with the id <code>animals</code>, it should reference an element inside the Shadow DOM with the id <code>listbox</code>. That's <code><div role="listbox" id="listbox"></code> here. <code>aria-activedescendant: opt1</code> works the same way.</p>
<p>This provides the flexibility to reference HTML elements inside the Shadow DOM, but some concerns are under discussion.</p>
<p>Please check out these issues if you're interested in learning more.</p>
<ul>
<li><a href="https://github.com/WICG/webcomponents/issues/1086">Reference Target Tracking Issue Ā· Issue #1086 Ā· WICG/webcomponents</a></li>
<li><a href="https://github.com/WICG/webcomponents/issues/1111">Reference Target "phase 2": seeking feedback and use cases Ā· Issue #1111 Ā· WICG/webcomponents</a></li>
</ul>
<h3 id="conclusion">Conclusion</h3>
<p>Reference Target for Cross-root ARIA enables us to reference HTML elements inside the Shadow DOM. This makes developing accessible Web Components easier, especially for UI component libraries and design systems. OpenUI is working on <a href="https://github.com/openui/design-system">The OpenUI Design System</a>, and this feature will be valuable for that project.</p>
<p>I recommend trying this feature in Chrome Canary and providing feedback to the <a href="https://github.com/WICG/webcomponents">Web Components CG</a>.</p>
Speculation rules improvements
2025-12-03T00:00:00Z
https://htmhell.dev/adventcalendar/2025/3/
by Barry Pollard<br><p>The <a href="https://developer.mozilla.org/docs/Web/API/Speculation_Rules_API">Speculation Rules API</a> allows you to speed up future navigations by prefetching or even prerendering URLs in advance of a user actually clicking a link. When the link is clicked, the speculation is used, and the user experiences a faster load than if no speculation was used.</p>
<p><a href="https://schepp.dev/">Schepp</a> covered <a href="https://htmhell.dev/adventcalendar/2024/28/">the API in last year's post</a> and discussed the <code>eagerness</code> value which allowed you to, for example, hover over a link to speculatively prerender it with the simple addition of this rule to your HTML:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"prerender"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"/*"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token string">"eagerness"</span><span class="token operator">:</span> <span class="token string">"moderate"</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>This hover technique is a common functionality offered by libraries, plugins, and frameworks, but now this is baked right into the browser. Chromium-based browsers only for now, but it is being worked on by Safari and Firefox, and as a progressive enhancement, nothing is broken on non-supporting browsers ā they just don't benefit from the performance improvement.</p>
<p>Using mouse hover as a signal works well on desktop, but what about mobile, where "hover" is not really a thing? At least in browser terms ā I'm sure many of us have hovered over a link with our fingers, but as that is not detected by the browser as an event, we can't act upon it.</p>
<p>Well, solving that is just one of many improvements the API has seen in the last year that we'll cover in this post.</p>
<h2 id="improved-mobile-viewport-heuristics">Improved mobile viewport heuristics</h2>
<p>Since hover does not exist, we have updated the <code>eagerness: moderate</code> on mobile to instead look at the following heuristics:</p>
<ul>
<li>Anchor links within 30% vertical distance from the previous pointer down.</li>
<li>Anchor links at least 0.5Ć as big as the largest anchor in the viewport.</li>
<li>The browser waits 500 milliseconds after the user stopped scrolling.</li>
</ul>
<p>The aim is to avoid, as much as possible, over-speculating links that users are less likely to click. For example, because they are still scrolling, or they are very small links (terms and conditions and the like!).</p>
<p>You can see this in action in the following video where the <a href="https://almanac.httparchiv.org/">Web Almanac</a> prerenders same-origin links with <code>moderate</code> eagerness:</p>
<figure>
<video controls="" playsinline="" muted="" loop="">
<source src="https://htmhell.dev/images/advent2025/speculation-rules-mobile-viewport-demo.mp4" width="2420" height="1320" />
</video>
<figcaption><p>An example of scrolling down a mobile page triggering speculations.</p></figcaption>
</figure>
<script>
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
if (!mediaQuery.matches) {
document.querySelector('video').autoplay = true;
}
</script>
<p>In the video, you can see that as you scroll down the page, links are being successfully prerendered, ready for the user to browse to the page.</p>
<p class="highlight"><strong>Note:</strong> Speculating has a cost. To both your users and for sites with potential increased traffic and resulting infrastructure usage. Always weigh those costs against the benefit to the user. Additionally, the are risks for more complex sites for speculating. See the <a href="https://developer.chrome.com/docs/web-platform/implementing-speculation-rules">Guide to implementing speculation rules for more complex sites</a> for more information.</p>
<p>To conserve memory, Chrome keeps up to two speculations in memory at a time. As the user scrolls further and new links ā which are more likely to be clicked on ā enter the viewport, the old prerenders are cancelled. These links can be re-speculated, for example, when the user scrolls back up, in which case they can be fetched from the HTTP cache. That way, they prerender even faster.</p>
<h2 id="eager-eagerness-improvements"><code>eager</code> eagerness improvements</h2>
<p>The <code>eager</code> value has also been changed. It now offers an option somewhere between <code>immediate</code>, where links are speculated as soon as possible, and <code>moderate</code>.</p>
<p>On desktop, <code>moderate</code> rules trigger after a 10 millisecond hover. On mobile, we consider <em>all</em> links in the viewport after the user has stopped scrolling for 100 milliseconds, rather than the more restrictive set of heuristics above.</p>
<p>One common technique is to prefetch the HTML document with <code>eager</code> value as that is often relatively cheap. Then upgrade this <code>prefetch</code> to a full <code>prerender</code> on <code>moderate</code> when you have more signals that the users may click on the link, and so think it's worthwhile to speculatively start to render the page in full, as prerender has more costs including downloading subresources and using memory and CPU needed to render the page.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"prefetch"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"/*"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token string">"eagerness"</span><span class="token operator">:</span> <span class="token string">"eager"</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token string">"prerender"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"/*"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token string">"eagerness"</span><span class="token operator">:</span> <span class="token string">"moderate"</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>In addition you can also restrict the links considered for speculating using the <code>where</code> object.</p>
<h2 id="further-improvements-to-the-api">Further improvements to the API</h2>
<p>I'll close out with a sneak peek into the future, as one further improvement being worked on is a middle ground between the <code>prefetch</code> and <code>prerender</code>. This is useful for those sites concerned with any unintended consequences with fully prerendering a page.</p>
<p>Executing JavaScript may trigger analytics, change state, or cause other changes that should not happen until the page is actually viewed. While it is possible to <a href="https://developer.chrome.com/docs/web-platform/prerender-pages#detect-prerender-in-javascript">make JavaScript prerender-aware</a>, or <a href="https://developer.chrome.com/docs/web-platform/prerender-pages#hold-back-other-content">holdback scripts until the page is navigated to</a>, this can involve a lot of efforts, particularly for large sites with lots of dependencies managed by many teams.</p>
<p>To help with this, a new <strong>Prender Until Script</strong> option is currently available behind a flag in Chrome (<code>chrome://flags/#prerender-until-script</code>). As its name suggests, it will start prerendering a page, but pause when it encounters a synchronous <code><script></code> element. Scripts with the <code>async</code> or <code>defer</code> attribute (or <code>module</code> scripts which are <code>defer</code> by default) will be downloaded but not executed until the page is navigated to.</p>
<p>This means:</p>
<ul>
<li>Pages without any JavaScript can be fully prerendered.</li>
<li>Pages with only async/deferred JavaScript can be fully prerendered, with JavaScript executed on navigation.</li>
<li>Pages with only sync <code><script></code> JavaScript can start prerender, but pause before any <code><script></code> causes any intended consequences. They will continue to download subresources (thanks to the <a href="https://web.dev/articles/preload-scanner">preload scanner</a>), so they still have a significant performance benefit over <code>prefetch</code>.</li>
</ul>
<p>After enabling the flag, you can use this new mode in exactly the same way as <code>prefetch</code> and <code>prerender</code>:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"prerender_until_script"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"/*"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token string">"eagerness"</span><span class="token operator">:</span> <span class="token string">"moderate"</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>This enhancement should be released next year. In the meantime, have a play and <a href="https://bsky.app/profile/tunetheweb.com">let me know</a> how you get on!</p>
<h2 id="conclusion">Conclusion</h2>
<p>The Speculation Rules API continues to improve with new options and features to help site owners deliver fast, HTML-driven websites!</p>
<p>We've even <a href="https://github.com/matuzo/HTMHell/pull/225">added speculation rules to this site</a>. As a light, static, HTML-driven site, it's the perfect type of site for this API. It was already a very fast site, but the addition of this API should make it even faster, especially on slower networks.</p>
<p>Finally, with our first signs of cross-browser adoption of the API, I'd speculate (boom! boom!) 2026 will be another bumper year for the API and for users of sites that implement it.</p>
Using the Ancient Evils for Debugging
2025-12-02T00:00:00Z
https://htmhell.dev/adventcalendar/2025/2/
by Manuel Strehl<br><p>Deep down in the dark voids of HTML specs long gone sleeps a terrifying thing. Imagine, if you will, a DOM node so mighty, that it can change the <code>content-type</code> of parts of the document. An HTML element that makes the parser tremble and withdraw, and that cannot be stopped even by its own end tag.</p>
<p>The wise people of W3C try to keep the knowledge of this terror away from the mere mortalsā eyes to spare us the danger of its madness. They advise us not to use the magic tag name that is the incantation for this ancient malice.</p>
<p>We will, of course, do exactly this today. Weāll take a deep look at the</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plaintext</span><span class="token punctuation">></span></span></span></code></pre>
<p>element and what fun things we can use it for.</p>
<h2 id="a-quick-warning">A Quick Warning</h2>
<p>This being said, Iād like to point out one important thing: Do not use this element in production. The HTML living standard is <a href="https://html.spec.whatwg.org/multipage/obsolete.html#plaintext">quite clear about this</a>:</p>
<blockquote>
<p>Elements in the following list are entirely obsolete, and must not be used by authors: [...] <code>plaintext</code></p>
</blockquote>
<p>So, what does <code><plaintext></code> do that earned it its place on HTMLās list of deprecated elements? In a nutshell, it ends the HTML parser and instructs the browser to interpret <em>everything</em> following as plain text.</p>
<p>That is to be taken literally. Really everything, including any closing <code></plaintext></code> or <code></html></code>, will be printed as if a rogue, unclosed <code><pre></code> would suddenly go haywire and slurp up the rest of the page. By the way, this makes <code><plaintext></code> the only non-empty element that has no end tag at all.</p>
<h2 id="what-do-we-use-this-power-for?">What Do We Use this Power For?</h2>
<p>On first sight that sounds like a really stupid superpower. On second sight, it still does. We look into how that element became part of HTML below. But now we will use it for one specific purpose: debugging server-side code.</p>
<p>Of course, specialized debuggers like XDebug for PHP or built-in error pages in frameworks like Django take over the heavy lifting here. And even the good olā <code>print "<script>console.log('here!')</script>"</code> is often helpful. Those tools should be high up in your utility belt.</p>
<p>But imagine this: You are deep in your code, chasing an elusive bug that affects only part of the HTML output, and you want to spot on the rendered page exactly where it shows up. The fastest way is to put a quick <code><plaintext></code> close to the offending place, reload the page, and presto! Just scan down to where the markup starts to show through.</p>
<p>This is especially useful to access formatted debugging output. A <code>var_dump()</code> in PHP, for example. Or an <code>error.stack</code> stack trace in NodeJS. Slap a <code><plaintext></code> in front of it before writing it to the HTML output, so that the string is immediately readable:</p>
<pre class="language-php"><code class="language-php"><span class="token php language-php"><span class="token delimiter important"><?php</span><br /><span class="highlight-line"><span class="token comment"># TODO delme!</span></span><br /><span class="highlight-line"><span class="token keyword">echo</span> <span class="token string single-quoted-string">'<plaintext>'</span><span class="token punctuation">;</span> <span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token variable">$strange_variable</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></span></code></pre>
<p><img src="https://htmhell.dev/adventcalendar/2025/2/debug_php.png" alt="A screenshot of the HTMHell website where the lower part shows a PHP variable output followed by the siteās markup instead of the rendered HTML" /></p>
<p>If youāre working on an expressJS application, it could look like this:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">try</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token function">some_method</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> response<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><plaintext></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>error<span class="token punctuation">.</span>stack<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p><img src="https://htmhell.dev/adventcalendar/2025/2/debug_express.png" alt="A screenshot of the same HTMHell website as above with the lower part showing a JS error stack followed by the siteās markup instead of the rendered HTML" /></p>
<h2 id="the-history-behind-this-evil">The History behind this Evil</h2>
<p>How ended this seemingly fringe feature up in all mainstream browsers? It was indeed there from the very beginning of HTML as this <a href="https://www.w3.org/History/19921103-hypertext/hypertext/WWW/MarkUp/Tags.html">historic W3C document</a> from 1992 proves:</p>
<blockquote class="blockquote-no-quotes">
<p><strong>Plaintext</strong></p>
<p>This tag indicates that all following text is to be taken litterally [!], up to the end of the file. Plain text is designed to be represented in the same way as example XMP text, with fixed width character and significant line breaks. Format:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>PLAINTEXT</span><span class="token punctuation">></span></span></span></code></pre>
</blockquote>
<p>This tag allows the rest of a file to be read efficiently without parsing. Its presence is an optimisation. There is no closing tag.</p>
<p>This also tells us the reason for its invention. Back at the time Sir Tim Berners-Leeās high-end NeXT PC that he used to write the first web browser had a quarter of the power of a hand-me-down 2009 smartphone. It was important to optimize wherever you could.</p>
<p>Given that the early WWW was meant as a place to share scientific information, having a large blob of plain text as part of your fancy new HTML page was relatively common. The possibility to end the costly HTML parser and fall back to simply printing the remainder of the file as plain text was a powerful tool.</p>
<figure style="margin-block-end:2.4rem">
<img src="https://htmhell.dev/adventcalendar/2025/2/screenshot_1992.png" alt="partial screenshot of a browser window. The address bar is shown, and the content of the page looks like a window of an old desktop OS." />
<figcaption>Screenshot: This is a rendering of <a href="https://www.w3.org/History/19921103-hypertext/hypertext/WWW/MarkUp/Connolly/structure5.html">a 1992 example for the use of the <code><plaintext></code> element</a> by <a href="https://worldwideweb.cern.ch/">an emulation of the very first web browser at CERN</a>. (I had to trick it a bit, though, because the file that the emulator would try to access returned a 404 error.)</figcaption>
</figure>
<p>Such a feature isnāt unique to HTML, either. For example, the programming language Perl uses a <a href="https://perldoc.perl.org/perldata#Special-Literals">special marker</a> to tell the Perl parser to stop processing the remainder of the file:</p>
<pre class="language-perl"><code class="language-perl"><span class="highlight-line"><span class="token keyword">print</span> <span class="token string">'this is Perl code'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">__END__</span><br /><span class="highlight-line">cout <span class="token operator"><<</span> <span class="token string">'this isnāt anymore'</span><span class="token punctuation">;</span></span></code></pre>
<p>(Perl programmers use this not for performance reasons but to embed additional data into their programs.)</p>
<p>Of course, nowadays, in the face of multi-megabyte JS payloads, this optimization has become completely unnecessary.</p>
<h2 id="how-safe-are-we?">How Safe Are We?</h2>
<p>But the element <em>is</em> still looming in all browsers, so it's worth keeping a bit of a working knowledge of it at the back of our minds.</p>
<p>To give you an example of how this feature could be mis-used, assume a comment function on a blog, where the commenter was able to smuggle in the string <code><plaintext></code>. As good developers we know never to trust usersā input, so we put the comment through a sanitizer. Letās take a look at where things can go south from here.</p>
<p>We use the test string</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plaintext</span><span class="token punctuation">></span></span>world!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>plaintext</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>to check how several sanitizer libraries react to it.</p>
<h3 id="there-goes-the-sanity!">There Goes the Sanity!</h3>
<p>We run each sanitizer in the most minimal configuration that produces any output. This is by design: sanitizers are security products. They should produce safe output by default.</p>
<p>The results are quite surprising, though. (Click on a libraryās name below to reveal the horrifying details.) You will notice that no two libraries (apart from the Sanitizer API and DOMPurify, where the former was directly inspired by the latter) agree on how to sanitize our test string.</p>
<details>
<summary><strong>The new HTML Sanitizer API as implemented in Firefox</strong></summary>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/parseHTML_static">developer.mozilla.org/en-US/docs/Web/API/Document/parseHTML_static</a></p>
<p><strong>Code:</strong></p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>Document<span class="token punctuation">.</span><span class="token function">parseHTML</span><span class="token punctuation">(</span><span class="token constant">TEST_STRING</span><span class="token punctuation">)</span><span class="token punctuation">.</span>body<span class="token punctuation">.</span>innerHTML<span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>The approach of this API is to somehow get the nesting correct again according to the HTML5 parser spec. That is, close the <code><p></code> and <code><b></code> tags, then re-open the <code><b></code> tag as the spec suggests. The API does not deal with the special semantics of <code><plaintext></code> at all, though.</p>
<p><strong>Result:</strong></p>
<p>We end up with a string that has the <code><plaintext></code> element and its content stripped away:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
</details>
<details>
<summary><strong>Poor manās DOM sanitizing</strong></summary>
<p><strong>Code:</strong></p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">const</span> div <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">div<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token constant">TEST_STRING</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>div<span class="token punctuation">.</span>innerHTML<span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>For this test we set the test string via <code>HTMLElement.innerHTML = test_string</code> and read it again via <code>.innerHTML</code>. Chrome and Firefox show the same result. This does not really sanitize anything. But we include it in the list, because it demonstrates what the JS engine will do to the test string when interpreting it as HTML.</p>
<p><strong>Result:</strong></p>
<p>The result is a mangled version of the original, which will have double-encoded content in the still retained <code><plaintext></code> element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plaintext</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>world!<span class="token entity named-entity" title="<">&lt;</span>/plaintext<span class="token entity named-entity" title=">">&gt;</span><span class="token entity named-entity" title="<">&lt;</span>/b<span class="token entity named-entity" title=">">&gt;</span><span class="token entity named-entity" title="<">&lt;</span>/p<span class="token entity named-entity" title=">">&gt;</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>plaintext</span><span class="token punctuation">></span></span></span></code></pre>
<p><em>NB:</em> We can trick the Sanitizer API into producing this output, too, if weāre careless with its configuration:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">Document<span class="token punctuation">.</span><span class="token function">parseHTML</span><span class="token punctuation">(</span><span class="token constant">TEST_STRING</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> sanitizer<span class="token operator">:</span> <span class="token punctuation">{</span> removeElements<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>body<span class="token punctuation">.</span>innerHTML</span></code></pre>
</details>
<details>
<summary><strong>HTML Tidy</strong></summary>
<p><a href="https://www.html-tidy.org/">www.html-tidy.org/</a></p>
<p><strong>Code:</strong></p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line"><span class="token builtin class-name">echo</span> -n <span class="token string">"<span class="token variable">$TEST_STRING</span>"</span> <span class="token operator">|</span> tidy</span></code></pre>
<p><strong>Result:</strong></p>
<p>The venerable Tidy replaces the <code><plaintext></code> with a <code><pre></code>. This is creative.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>pre</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>world!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>pre</span><span class="token punctuation">></span></span></span></code></pre>
</details>
<details>
<summary><strong>xss</strong></summary>
<p><a href="https://jsxss.com/">jsxss.com/</a></p>
<p><strong>Code:</strong></p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">import</span> xss <span class="token keyword">from</span> <span class="token string">'xss'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">xss</span><span class="token punctuation">(</span><span class="token constant">TEST_STRING</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p><strong>Result:</strong></p>
<p>A well-known JavaScript-based sanitizer with special focus on XSS prevention escapes only the <code><plaintext></code> tags and leaves everything else in place.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token entity named-entity" title="<">&lt;</span>plaintext<span class="token entity named-entity" title=">">&gt;</span>world!<span class="token entity named-entity" title="<">&lt;</span>/plaintext<span class="token entity named-entity" title=">">&gt;</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
</details>
<details>
<summary><strong>DOMPurify</strong></summary>
<p><a href="https://github.com/cure53/DOMPurify">github.com/cure53/DOMPurify</a></p>
<p><strong>Code:</strong></p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">import</span> <span class="token punctuation">{</span> <span class="token constant">JSDOM</span> <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'jsdom'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">import</span> DOMPurify <span class="token keyword">from</span> <span class="token string">'dompurify'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">const</span> purify <span class="token operator">=</span> <span class="token function">DOMPurify</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">JSDOM</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">.</span>window<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>purify<span class="token punctuation">.</span><span class="token function">sanitize</span><span class="token punctuation">(</span><span class="token constant">TEST_STRING</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p><strong>Result:</strong></p>
<p>The classic JS sanitizer chooses to remove the <code><plaintext></code> and all its ācontentā. (I put content in quotes, because <em>technically</em> everything after the start tag wouldāve been the <code><plaintext></code>ās content.) DOMPurify sees to it that the elements are properly closed. The result is identical to that of the Sanitizer API.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
</details>
<details>
<summary><strong>HTML Purifier</strong></summary>
<p><a href="http://htmlpurifier.org/">htmlpurifier.org/</a></p>
<p><strong>Code:</strong></p>
<pre class="language-php"><code class="language-php"><span class="token php language-php"><span class="token delimiter important"><?php</span><br /><span class="highlight-line"><span class="token variable">$config</span> <span class="token operator">=</span> <span class="token class-name static-context">HTMLPurifier_Config</span><span class="token operator">::</span><span class="token function">createDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token variable">$purifier</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HTMLPurifier</span><span class="token punctuation">(</span><span class="token variable">$config</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token function">printf</span><span class="token punctuation">(</span><span class="token variable">$purifier</span><span class="token operator">-></span><span class="token function">purify</span><span class="token punctuation">(</span><span class="token constant">TEST_STRING</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></span></code></pre>
<p><strong>Result:</strong></p>
<p>The top dog in the PHP world takes a slightly different approach. It removes only the element itself. (Note the āworld!ā remaining intact.)</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>helloworld!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
</details>
<details>
<summary><strong>Symfony HtmlSanitizer</strong></summary>
<p><a href="https://symfony.com/html-sanitizer">symfony.com/html-sanitizer</a></p>
<p><strong>Code:</strong></p>
<pre class="language-php"><code class="language-php"><span class="token php language-php"><span class="token delimiter important"><?php</span><br /><span class="highlight-line"><span class="token keyword">use</span> <span class="token package">Symfony<span class="token punctuation">\</span>Component<span class="token punctuation">\</span>HtmlSanitizer<span class="token punctuation">\</span>HtmlSanitizer</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">use</span> <span class="token package">Symfony<span class="token punctuation">\</span>Component<span class="token punctuation">\</span>HtmlSanitizer<span class="token punctuation">\</span>HtmlSanitizerConfig</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token variable">$config</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">HtmlSanitizerConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">allowSafeElements</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token variable">$sanitizer</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HTMLSanitizer</span><span class="token punctuation">(</span><span class="token variable">$config</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token function">printf</span><span class="token punctuation">(</span><span class="token variable">$sanitizer</span><span class="token operator">-></span><span class="token function">sanitize</span><span class="token punctuation">(</span><span class="token constant">TEST_STRING</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></span></code></pre>
<p><strong>Result:</strong></p>
<p>Symfonyās sanitizer has a fascinating way of moving tags around. Interesting, but at least weāve got all elements properly closed, including the uncloseable <code>plaintext</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plaintext</span><span class="token punctuation">></span></span>world!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>plaintext</span><span class="token punctuation">></span></span></span></code></pre>
</details>
<details>
<summary><strong>xmllint</strong></summary>
<p><a href="https://gnome.pages.gitlab.gnome.org/libxml2/xmllint.html">gnome.pages.gitlab.gnome.org/libxml2/xmllint.html</a></p>
<p><strong>Code:</strong></p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line"><span class="token builtin class-name">echo</span> -n <span class="token string">"<span class="token variable">$TEST_STRING</span>"</span> <span class="token operator">|</span> xmllint --html -</span></code></pre>
<p><strong>Result:</strong></p>
<p>This libxml-based tool produces a warning about an āinvalid tag plaintextā but keeps the markup completely unchanged:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plaintext</span><span class="token punctuation">></span></span>world!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>plaintext</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
</details>
<details>
<summary><strong>Mozilla Bleach</strong></summary>
<p><a href="https://github.com/mozilla/bleach">github.com/mozilla/bleach</a></p>
<p><strong>Code:</strong></p>
<pre class="language-py"><code class="language-py"><span class="highlight-line"><span class="token keyword">import</span> bleach</span><br /><span class="highlight-line"><span class="token keyword">print</span><span class="token punctuation">(</span>bleach<span class="token punctuation">.</span>clean<span class="token punctuation">(</span>TEST_STRING<span class="token punctuation">)</span><span class="token punctuation">)</span></span></code></pre>
<p><strong>Result:</strong></p>
<p>Python developers who reach for this library will have everything but the <code><b></code> escaped.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token entity named-entity" title="<">&lt;</span>p<span class="token entity named-entity" title=">">&gt;</span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token entity named-entity" title="<">&lt;</span>plaintext<span class="token entity named-entity" title=">">&gt;</span>world!<span class="token entity named-entity" title="<">&lt;</span>/plaintext<span class="token entity named-entity" title=">">&gt;</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token entity named-entity" title="<">&lt;</span>/p<span class="token entity named-entity" title=">">&gt;</span></span></code></pre>
</details>
<details>
<summary><strong>OWASP Java HTML Sanitizer</strong></summary>
<p><a href="https://github.com/OWASP/java-html-sanitizer/">github.com/OWASP/java-html-sanitizer/</a></p>
<p><strong>Code:</strong></p>
<pre class="language-java"><code class="language-java"><span class="highlight-line"><span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>owasp<span class="token punctuation">.</span>html<span class="token punctuation">.</span></span><span class="token class-name">PolicyFactory</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>owasp<span class="token punctuation">.</span>html<span class="token punctuation">.</span></span><span class="token class-name">Sanitizers</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Sanitize</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token class-name">PolicyFactory</span> policy <span class="token operator">=</span> <span class="token class-name">Sanitizers</span><span class="token punctuation">.</span>FORMATTING<span class="token punctuation">.</span><span class="token function">and</span><span class="token punctuation">(</span><span class="token class-name">Sanitizers</span><span class="token punctuation">.</span>LINKS<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token class-name">String</span> safe <span class="token operator">=</span> policy<span class="token punctuation">.</span><span class="token function">sanitize</span><span class="token punctuation">(</span>TEST_STRING<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>safe<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p><strong>Result:</strong></p>
<p>The staple HTML sanitizer in the Java world escapes everything and does strange things to the end tags, but at least the <code><plaintext></code> is gone.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>helloworld!<span class="token entity named-entity" title="<">&lt;</span>/plaintext<span class="token entity named-entity" title=">">&gt;</span><span class="token entity named-entity" title="<">&lt;</span>/b<span class="token entity named-entity" title=">">&gt;</span><span class="token entity named-entity" title="<">&lt;</span>/p<span class="token entity named-entity" title=">">&gt;</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span></span></code></pre>
</details>
<details>
<summary><strong>Ammonia as configured by nh3</strong></summary>
<p><a href="https://github.com/rust-ammonia/ammonia">github.com/rust-ammonia/ammonia</a>, <a href="https://nh3.readthedocs.io/">nh3.readthedocs.io/</a></p>
<p><strong>Code:</strong></p>
<pre class="language-py"><code class="language-py"><span class="highlight-line"><span class="token keyword">import</span> nh3</span><br /><span class="highlight-line"><span class="token keyword">print</span><span class="token punctuation">(</span>nh3<span class="token punctuation">.</span>clean<span class="token punctuation">(</span>TEST_STRING<span class="token punctuation">)</span><span class="token punctuation">)</span></span></code></pre>
<p>This Rust-based sanitizer advertises its speed and conformance with the HTML spec.</p>
<p><strong>Result:</strong></p>
<p>The result is close but still different to what browsers will do. In this case, itās the <code><b></code> tag that would not extend over the content of the <code><plaintext></code> element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>world!<span class="token entity named-entity" title="<">&lt;</span>/plaintext<span class="token entity named-entity" title=">">&gt;</span><span class="token entity named-entity" title="<">&lt;</span>/b<span class="token entity named-entity" title=">">&gt;</span><span class="token entity named-entity" title="<">&lt;</span>/p<span class="token entity named-entity" title=">">&gt;</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span></span></code></pre>
</details>
<hr />
<p>With <strong>11</strong> sanitizing methods we managed to produce <strong>10</strong> different outputs!</p>
<p>Just to be crystal clear here: we are not criticizing the result of any of these libraries. Each one has a good reason to do what they do. And each one serves a slightly different purpose.</p>
<p>But it emphasizes the point that one should be absolutely sure about the aim of a chosen sanitizer and the extent as to which it will change its input. Is the library for removing only potentially dangerous things but keeping as much HTML intact as possible? Is it to scrape all HTML off the string, or to only escape HTML-special characters? The results will differ tremendously.</p>
<p>We enter the danger zone when mixing several tools together without taking a cautious look first.</p>
<p>For example, look at how DOMPurify and HTML Purifier would interact in a potentially hazardous way. DOMPurify would remove any <code><plaintext></code> including its content. A later check for any malicious payload would be negative. HTML Purifier on the other hand just strips the <code><plaintext></code> tag, while its content remains on the page. If weād trust the previous DOMPurify result, weād be surprised by sudden new content being placed verbatim in the HTML code.</p>
<p>If one library is used for input validation and another one for output quoting, thatās a <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">cross-site scripting</a> disaster waiting to happen, unless we know <em>exactly</em> what weāre doing.</p>
<h2 id="letting-the-evil-sleep-again">Letting the Evil Sleep Again</h2>
<p>In the case of <code><plaintext></code> itself we are most likely in a safe place. Since <code><plaintext></code> has built-in HTML escaping, doing something dangerous with it is severely limited. It takes quite rare a constellation of errors and oversights to appear together in order to run malicious code.</p>
<p>For the sake of the argument, letās create such a constellation. Assume that you embed a Content-Security Policy on your site <a href="https://w3c.github.io/webappsec-csp/#meta-element">in a <code><meta></code> element</a> instead of an HTTP header:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>script-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>This prevents loading 3rd party scripts sufficiently. If an attacker finds a possibility to load HTML prior to this element, they can nullify the CSP:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>https://example.com/malicious.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plaintext</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>script-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>But again, for this to really have any effect, several things must come together:</p>
<ul>
<li>The attacker must be able to place HTML in the <code><head></code> (because CSP meta tags can only be used there)</li>
<li>The CSP is not set via HTTP</li>
<li>The complete remainder of the page is converted to <code>text/plain</code>, which makes this definitively not a stealthy attack</li>
</ul>
<p>So we can conclude: It is important to know about <code><plaintext></code>. But if we follow tried and tested security rules (for example the <a href="https://owasp.org/www-project-application-security-verification-standard/">OWASP Application Security Verification Standard</a>), we will remain safe from this ancient evil.</p>
<p><em>Iād like to thank <a href="https://hachyderm.io/@evilpie">Tom Schuster</a>, Christian Vogl, and Daniela Strehl for valuable input to this article and <a href="https://elisehe.in/">Elise Hein</a> for an extremely helpful review.</em></p>
Top layer troubles: popover vs. dialog
2025-12-01T00:00:00Z
https://htmhell.dev/adventcalendar/2025/1/
by Stephanie Eckles<br><p>Have you ever tried to set <code>z-index: 9999</code> to solve element layering issues? If so, youāve been fighting a fundamental CSS concept of <em>stacking contexts</em>.</p>
<p>The stacking context defines the order things are placed in the third dimension, or the āzā axis. Think of the z-axis as layers of DOM elements between the root of the stacking context within the viewport, and you, the user, looking through the browser viewport.</p>
<p><img src="https://htmhell.dev/images/advent2025/top-layer/stacking-context.jpg" alt="A diagram showing a series of four diamonds representing element layers arranged to create a stack. To the left is an arrow point up from the bottom of the stack, with the label of 'z-axis'. At the bottom of the arrow is the label of 'viewport' and at the top is the label of 'user', demonstrating how stacking contexts build element layers along the z-axis between the viewport and the user." /></p>
<p>An element can only be re-layered within the same stacking context. While <code>z-index</code> is the tool to do it, the failure happens due to a change in stacking contexts. This can happen in a few ways, such as a fixed or sticky positioned element, or combining position absolute or relative along with a z-index, among others <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Stacking_context">as listed on MDN</a>.</p>
<p>A modern web feature is the ā<a href="https://developer.mozilla.org/en-US/docs/Glossary/Top_layer">top layer</a>,ā which is guaranteed to be the top-most layer above any other stacking context. It spans the entire viewport, although elements existing in the top layer may have smaller visible dimensions.</p>
<p>Promoting an element to the top layer breaks it free of any stacking context it may otherwise exist within.</p>
<p>Although the top layer directly addresses a CSS-related issue, there is currently no property available to promote an element to the top layer. Instead, certain elements and conditions gain access to the top layer, including native dialogs via <code>showModal()</code> and elements designated as popovers.</p>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Popover_API/Using">Popover API</a> is a newly available HTML feature that enables you to create declarative non-modal overlay elements. The ability to escape any stacking context by gaining top layer access is usually a desirable feature of choosing the Popover API. However, thereās a sneaky potential conflict to be aware of before hastily opting for this native upgrade.</p>
<h2 id="setting-the-scene">Setting the scene</h2>
<p>Picture this, the web, 2025: your web app includes a notification service that displays via ātoastā messages. You know, those pop-up messages that usually appear in a corner or other location with a lower likelihood of obstructing any other UI.</p>
<p>Usually these toast notifications are for real-time alerts of things like a successful save, or errors such as a failed form submit. They are sometimes time limited, or include a dismissal mechanism like a close button. Sometimes they include an additional action, such as to āRetryā for re-submitting a failed workflow.</p>
<p>Since your app is hip to the times, youāve recently decided to upgrade the toasts to use the Popover API. This enables you to place the toast component anywhere in the app structure, and not have to hack around stacking context issues. After all, toasts absolutely must appear over everything else, so the top layer access afforded via popovers makes sense!</p>
<p>You ship the enhancement, proud of your work.</p>
<p>Later that week, you get an urgent bug report. Not just any bug report, but an accessibility violation.</p>
<h2 id="dialog-vs-popover">Dialog vs. popover</h2>
<p>Because your app is hip, you also had previously upgraded to native HTML dialogs. That was an incredible upgrade because you got to reduce a JavaScript dependency in favor of a native web feature. Thatās another reason you were excited to enhance your toasts to use popover as well.</p>
<p>So, what was the bug? A keyboard user was in a workflow that involved a dialog. While the dialog was open, a background process caused a toast notification to trigger. The notification was for an error, and required user interaction.</p>
<p>The bug occurred when the keyboard user tried to tab into the toast, which they were able to visually see above the dialog backdrop. Unfortunately, their focus never successfully entered the toast, and instead seemed to skip over it, jumping unexpectedly to the browser UI instead.</p>
<p>You can experience this error yourself in this CodePen, where by using the tab key, you too will never be able to access the toast. You can also try it out using a screen reader, and notice that the virtual cursor is also unable to get into the toast.</p>
<p class="codepen" data-height="400" data-default-tab="result" data-slug-hash="RNrJxyo" data-pen-title="conflicting" data-user="5t3ph" data-token="ba02f03947d400a202a02e01c643eaea" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/5t3ph/pen/RNrJxyo/ba02f03947d400a202a02e01c643eaea">
conflicting</a> by Stephanie Eckles (<a href="https://codepen.io/5t3ph">@5t3ph</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<p>If you are able to click on the popover, you may think itās at least working with that method. But shortly weāll learn that thing arenāt always as they seem.</p>
<h2 id="why-the-toast-popover-is-unreachable">Why the toast popover is unreachable</h2>
<p>While top layer allows beating out standard stacking contexts, items that exist within the top layer are still affected by layering order. The most recently added top layer item appears over previous top layer items. This is why the toast was visually appearing over the dialogās backdrop.</p>
<p>So if the popover is <em>visually</em> available, why is it unreachable via keyboard or a screen readerās virtual cursor?</p>
<p>The reason has to do with the popover competing with a <em>modal</em> dialog. When the native HTML dialog is launched via <code>showModal()</code>, the page outside of the dialog becomes <em>inert</em>. The state of <em>inert</em> is a necessary accessibility behavior, which results in isolating the dialog contents, and prevents both tab and virtual cursor access to the background page.</p>
<p>The bug is due to the toast popover being part of the background pageās DOM. This means it has been made inert due to being outside of the DOM boundary of the dialog.</p>
<p>But, due to top layer order, since it was created after the dialog was opened, it is confusingly sitting visually over the dialog.</p>
<p>If you thought clicking the popover was working, itās in fact not, even though the popover does dismiss. What is really happening is that you are triggering the <em>light dismiss</em> behavior of the popover. This means it is closing because you are technically clicking outside of itās boundaries, since the dialog is capturing the click instead.</p>
<p>So, while the popover was dismissed, the āRetryā button was <em>not</em> actually clicked, meaning any associated event listener was not triggered.</p>
<p>Even if you created an automated test to check your alert functionality specifically when a dialog was open, the automated test may have had a false positive because it triggered a programmatic click on the toast button. That pseudo click was falsely getting around the issue created from the dialog causing the page to be inert.</p>
<h2 id="regaining-popover-access">Regaining popover access</h2>
<p>The solve is two-fold:</p>
<ol>
<li>move the popover physically inside the DOM of the dialog</li>
<li>ensure use of <code>popover="manual"</code> to prevent clicks inside the dialog from prematurely triggering light dismiss on the popover</li>
</ol>
<p>Once weāve done both of those things, the popover is now both visually available and fully interactive via any method.</p>
<p class="codepen" data-height="400" data-default-tab="result" data-slug-hash="qEbKpJd" data-pen-title="conflicting" data-user="5t3ph" data-token="57e8ec936911cf8549df0df72c650650" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/5t3ph/pen/qEbKpJd/57e8ec936911cf8549df0df72c650650">
conflicting</a> by Stephanie Eckles (<a href="https://codepen.io/5t3ph">@5t3ph</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<h2 id="learnings-and-additional-considerations">Learnings and additional considerations</h2>
<p>What weāve learned is that you will need to work out a mechanism to launch popovers from within dialogs if your website or app has the possibility of both displaying at once, and they have independent timelines.</p>
<p>Alternatively, you could opt to suppress background page popovers until the dialog closes. This may not be ideal if notifications require timely interaction, or if the dialog contents have the potential to trigger a toast.</p>
<p>Another issue you may need to handle for, besides visibility and interactivity, is if the popover needs to outlive the dialog. As in, it needs to remain open - perhaps to keep waiting for the user to take action - once the dialog is closed.</p>
<p>While I am a huge proponent of using native platform features, and I think popover in particular is an incredible feature, sometimes conflict points canāt be entirely avoided. In fact, you may have already had to contend with a similar conflict against the inert behavior of a modal dialog. So, this article may mostly be a warning to not entirely rip out your previous custom popover architecture <em>if</em> you have this potential issue of displaying background popovers and modal dialogs simultaneously.</p>
<p>If this is an issue that currently, or may in the future affect your work, keep an eye on <a href="https://github.com/whatwg/html/issues/9936">this HTML issue where solutions are being discussed</a>.</p>
<script async="" src="https://public.codepenassets.com/embed/index.js"></script>
datalists are more powerful than you think
2024-12-29T00:00:00Z
https://htmhell.dev/adventcalendar/2024/29/
by Alexis Degryse<br><p>I think we all know the <code><datalist></code> element (and if you donāt, itās ok). It holds a list of <code><option></code> elements, offering suggested choices for its associated input field.</p>
<p>Itās not an alternative for the <code><select></code> element. A field associated to a <code><datalist></code> can still allow any value that is not listed in the <code><option></code> elements.</p>
<p>Here is a basic example:</p>
<iframe height="400" style="width: 100%;" scrolling="no" title="Demo textual datalist on Codepen" src="https://codepen.io/twogrey/embed/preview/QwLEwvG?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
<p>Pretty cool, isn't it? But what happens if we combine <code><datalist></code> with less common field types, like <code>color</code> and <code>date</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>favorite-color<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>What is your favorite color?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>color<span class="token punctuation">"</span></span> <span class="token attr-name">list</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>colors-list<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>favorite-color<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>datalist</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>colors-list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>#FF0000<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>#FFA500<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>#FFFF00<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>#008000<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>#0000FF<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>#800080<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>#FFC0CB<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>#FFFFFF<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>#000000<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>datalist</span><span class="token punctuation">></span></span></span></code></pre>
<iframe height="400" style="width: 100%;" scrolling="no" title="Demo color type field with datalist on Codepen" src="https://codepen.io/twogrey/embed/preview/YPKWPao?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
<p>Colors listed in <code><datalist></code> are pre-selectable but the color picker is still usable by users if they need to choose a more specific one.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>event-choice<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>form-label col-form-label-lg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Choose a historical date<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>date<span class="token punctuation">"</span></span> <span class="token attr-name">list</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>events<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>event-choice<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>datalist</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>events<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Fall of the Berlin wall<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>1989-11-09<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Maastricht Treaty<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>1992-02-07<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Brexit Referendum<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2016-06-23<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>datalist</span><span class="token punctuation">></span></span></span></code></pre>
<iframe height="400" style="width: 100%;" scrolling="no" title="Demo date type field with datalist on Codepen" src="https://codepen.io/twogrey/embed/preview/XJrKJyB?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
<p>Same here: some dates are pre-selectable and the datepicker is still available.</p>
<p>Depending on the context, having pre-defined values can possibly speed up the form filling by users.</p>
<p>Please, note that <code><datalist></code> should be seen as a <strong>progressive enhancement</strong> because of some points:</p>
<ul>
<li>For Firefox (tested on 133), the <code><datalist></code> element is compatible only with textual field types (think about text, url, tel, email, number). There is no support for color, date and time.</li>
<li>For Safari (tested on 15.6), it has support for color, but not for date and time.</li>
<li>With some screen reader/browser combinations there are issues. For example, <a href="https://a11ysupport.io/tests/tech__html__datalist/html__datalist_element/convey_role/vo_macos/safari">suggestions are not announced in Safari</a> and it's not possible to navigate to the datalist with the <code>down arrow</code> key (until you type something matched with suggestions). Refer to <a href="https://a11ysupport.io/tech/html/datalist_element">a11ysupport.io</a> for more.</li>
</ul>
<h2 id="find-out-more">Find out more</h2>
<ul>
<li><a href="https://demo.agektmr.com/datalist/">datalist experiment</a> by <a href="https://bsky.app/profile/agektmr.com">Eiji Kitamura</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist">Documentation on MDN</a></li>
</ul>
Boost website speed with prefetching and the Speculation Rules API
2024-12-28T00:00:00Z
https://htmhell.dev/adventcalendar/2024/28/
by Schepp<br><p>Everybody loves fast websites, and everyone despises slow ones even more. Site speed significantly contributes to the overall user experience (UX), determining whether it feels positive or negative. To ensure the fastest possible page load times, itās crucial to design with performance in mind. However, performance optimization is an art form in itself. While implementing straightforward techniques like file compression or proper cache headers is relatively easy, achieving deeper optimizations can quickly become complex.</p>
<p>But what if, instead of solely trying to accelerate the loading process, we triggered it earlierāwithout the user noticing?</p>
<p>One way to achieve this is by prefetching pages the user might navigate to next using <code><link rel="prefetch"></code> tags. These tags are typically embedded in your HTML, but they can also be generated dynamically via JavaScript, based on a heuristic of your choice. Alternatively, you can send them as an <a href="https://www.debugbear.com/blog/resource-hints-rel-preload-prefetch-preconnect#resource-hints-in-http-headers">HTTP <code>Link</code> header</a> if you lack access to the HTML code but can modify the server configuration. Browsers will take note of the prefetch directives and fetch the referenced pages as needed.</p>
<aside class="info">
<p><em><strong>ā ļø Caveat:</strong> To benefit from this prefetching technique, you must allow the browser to cache pages temporarily using the <code>Cache-Control</code> HTTP header. For example, <code>Cache-Control: max-age=300</code> would tell the browser to cache a page for five minutes. Without such a header, the browser will discard the pre-fetched resource and fetch it again upon navigation, rendering the prefetch ineffective.</em></p>
</aside>
<p>In addition to <code><link rel="prefetch"></code>, Chromium-based browsers support <code><link rel="prerender"></code>. This tag is essentially a supercharged version of <code><link rel="prefetch"></code>. Known as "<a href="https://developer.chrome.com/blog/nostate-prefetch">NoState Prefetch</a>," it not only prefetches an HTML page but also scans it for subresourcesāstylesheets, JavaScript files, images, and fonts referenced via a <code><link rel="preload" as="font" crossorigin></code> ā loading them as well.</p>
<h3 id="the-speculation-rules-api">The Speculation Rules API</h3>
<p>A relatively new addition to Chromium browsers is the <strong>Speculation Rules API</strong>, which offers enhanced prefetching and enables actual prerendering of webpages. It introduces a JSON-based syntax for precisely defining the conditions under which preprocessing should occur.</p>
<p>Hereās a simple example of how to use it:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"prerender"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"urls"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"next.html"</span><span class="token punctuation">,</span> <span class="token string">"next2.html"</span><span class="token punctuation">]</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<aside class="info">
<p>Alternatively, you can place the JSON file on your server and reference it using an HTTP header: <code>Speculation-Rules: "/speculationrules.json"</code>.</p>
</aside>
<p>The above <code>list</code>-rule specifies that the browser should prerender the URLs <code>next.html</code> and <code>next2.html</code> so they are ready for instant navigation. The keyword <code>prerender</code> means more than fetching the HTML and subresourcesāit instructs the browser to fully render the pages in hidden tabs, ready to replace the current page instantly when needed. This makes navigation to these pages feel seamless.</p>
<p>Prerendered pages also typically score excellent Core Web Vital metrics. Layout shifts and image loading occur during the hidden prerendering phase, and JavaScript execution happens upfront, ensuring a smooth experience when the user first sees the page.</p>
<p>Instead of listing specific URLs, the API also allows for pattern matching using <code>where</code> and <code>href_matches</code> keys:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"prerender"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"/*"</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>For more precise targeting, CSS selectors can be used with the <code>selector_matches</code> key:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"prerender"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"selector_matches"</span><span class="token operator">:</span> <span class="token string">".navigation__link"</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>These rules, called <code>document</code>-rules, act on link elements as soon as the user triggers a <code>pointerdown</code> or <code>touchstart</code> event, giving the referenced pages a few milliseconds' head start before the actual navigation.</p>
<p>If you want the preprocessing to begin even earlier, you can adjust the <code>eagerness</code> setting:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"prerender"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"/*"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token string">"eagerness"</span><span class="token operator">:</span> <span class="token string">"moderate"</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p><strong>Eagerness values:</strong></p>
<ul>
<li><code>immediate</code>: Executes immediately.</li>
<li><code>eager</code>: Currently behaves like <code>immediate</code> but may be refined to sit between <code>immediate</code> and <code>moderate</code>.</li>
<li><code>moderate</code>: Executes after a 200ms hover or on <code>pointerdown</code> for mobile devices.</li>
<li><code>conservative</code> (default): Speculates based on pointer or touch interaction.</li>
</ul>
<p>For even greater flexibility, you can combine <code>prerender</code> and <code>prefetch</code> rules with different eagerness settings:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>speculationrules<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"prerender"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"/*"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token string">"eagerness"</span><span class="token operator">:</span> <span class="token string">"conservative"</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token string">"prefetch"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token string">"where"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"href_matches"</span><span class="token operator">:</span> <span class="token string">"/*"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token string">"eagerness"</span><span class="token operator">:</span> <span class="token string">"moderate"</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">]</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h3 id="limitations-and-challenges">Limitations and Challenges</h3>
<p>While the Speculation Rules API is powerful, it comes with some limitations:</p>
<ol>
<li><strong>Browser support:</strong> Only Chromium-based browsers support it. Other browsers lack this capability, so treat it as a progressive enhancement.</li>
<li><strong>Bandwidth concerns:</strong> Over-aggressive settings could waste user bandwidth. Chromium imposes limits to mitigate this: a maximum of 10 prerendered and 50 prefetched pages with <code>immediate</code> or <code>eager</code> eagerness.</li>
<li><strong>Server strain:</strong> Poorly optimized servers (e.g., no caching, heavy database dependencies) may experience significant load increases due to excessive speculative requests.</li>
<li><strong>Compatibility:</strong> Prefetching wonāt work if a Service Worker is active, though prerendering remains unaffected. Cross-origin prerendering requires explicit opt-in by the target page.</li>
</ol>
<p>Despite these caveats, the Speculation Rules API offers a powerful toolset to significantly enhance perceived performance and improve UX. So go ahead and try them out!</p>
<p><em>I would like to express a big thank you to the Webperf community for always being ready to help with great tips and expertise. For this article, I would like to thank Barry Pollard, Andy Davies, and Noam Rosenthal in particular for providing very valuable background information. ā¤ļø</em></p>
Misleading Icons: Icon-Only-Buttons and Their Impact on Screen Readers
2024-12-27T00:00:00Z
https://htmhell.dev/adventcalendar/2024/27/
by Alexander Muzenhardt<br><h2 id="introduction">Introduction</h2>
<p>Imagine youāre tasked with building a cool new feature for a product. You dive into the work with full energy, and just before the deadline, you manage to finish it. Everyone loves your work, and the feature is set to go live the next day.<br />
A few days later, you receive an email from a user who canāt access the new feature. The user points out that they donāt understand what the button does. What do they mean? You review your code, locate the button, and start digging into the problem.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>š<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="the-problem">The Problem</h2>
<p>You find some good resources explaining that there are people with disabilities who need to be considered in these cases. This is known as accessibility. For example, some individuals have motor impairments and cannot use a mouse. In this particular case, the user is visually impaired and relies on assistive technology like a screen reader, which reads aloud the content of the website or software. The button you implemented doesnāt have any descriptive text, so only the icon is read aloud. In your case, the screen reader says, āTear-Off Calendar buttonā. While it describes the appearance of the icon, it doesnāt convey the purpose of the button. This information is meaningless to the user. A button should always describe what action it will trigger when activated. Thatās why we need additional descriptive text.</p>
<h2 id="the-challenge">The Challenge</h2>
<p>Okay, you understand the problem now and agree that it should be fixed. However, you donāt want to add visible text to the button. For design and aesthetic reasons, sighted users should only see the icon. Is there a way to keep the button āicon-onlyā while still providing a meaningful, descriptive text for users who rely on assistive technologies like screen readers?</p>
<h2 id="the-solution">The Solution</h2>
<p>First, you need to give the button a descriptive name so that a screen reader can announce it.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Open Calendar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>š<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>The problem now is that the buttonās name becomes visible, which goes against your design guidelines. To prevent this, additional CSS is required.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> -1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border-width</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Open Calendar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>š<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>The CSS ensures that the text inside the span-element is hidden from sighted users but remains readable for screen readers. This approach is so common that well-known CSS libraries like TailwindCSS, Bootstrap, and Material-UI include such a class by default.</p>
<p>Although the text of the buttons is not visible anymore, the entire content of the button will be read aloud, including the icon ā something you want to avoid.</p>
<p>In HTML you are allowed to use specific attributes for accessibility, and in this case, the attribute <strong>aria-hidden</strong> is what you need. ARIA stands for āAccessible Rich Internet Applicationsā and is an initiative to make websites and software more accessible to people with disabilities.</p>
<p>The attribute <strong>aria-hidden</strong> hides elements from screen readers so that their content isnāt read. All you need to do is add the attribute <strong>aria-hidden</strong> with the value ātrueā to the icon element, which in this case is the āiā-element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Open Calendar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>icon<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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>š<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="alternative">Alternative</h2>
<p>An alternative is the attribute <strong>aria-label</strong>, which you can assign a descriptive, accessible text to a button without it being visible to sighted users. The purpose of <strong>aria-label</strong> is to provide a description for interactive elements that lack a visible label or descriptive text. All you need to do is add the attribute <strong>aria-label</strong> to the button. The attribute <strong>aria-hidden</strong> and the span-Element can be deleted.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Open Calendar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>š<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>With this adjustment, the screen reader will now announce āOpen calendar,ā completely ignoring the icon. This clearly communicates to the user what the button will do when clicked.</p>
<h2 id="which-option-should-you-use?">Which Option Should You Use?</h2>
<p>At first glance, the aria-label approach might seem like the smarter choice. It requires less code, reducing the likelihood of errors, and looks cleaner overall, potentially improving code readability.</p>
<p>However, the first option is actually the better choice. There are several reasons for this that may not be immediately obvious:</p>
<ul>
<li>Some browsers do not translate aria-label</li>
<li>It is difficult to copy aria-label content or otherwise manipulated it as text</li>
<li>aria-label content will not show up if styles fail to load</li>
</ul>
<p>These are just a few of the many reasons why you should be cautious when using the aria-label attribute. These points, along with others, are discussed in detail in the excellent article "<a href="https://ericwbailey.website/published/aria-label-is-a-code-smell">aria-label is a Code Smell</a>" by <a href="https://github.com/ericwbailey/ericwbailey.website">Eric Bailey</a>.</p>
<h2 id="the-first-rule-of-aria-use">The First Rule of ARIA Use</h2>
<p>The ā<a href="https://www.w3.org/TR/using-aria/#firstrule">First Rule of ARIA Use</a>ā states:</p>
<p>If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.</p>
<p>Even though the first approach also uses an ARIA attribute, it is more acceptable because <strong>aria-hidden</strong> only hides an element from screen readers. In contrast, <strong>aria-label</strong> overrides the standard HTML behavior for handling descriptive names. For this reason, following this principle, <strong>aria-hidden</strong> is preferable to <strong>aria-label</strong> in this case.</p>
<h2 id="browser-compatibility">Browser compatibility</h2>
<p>Both <strong>aria-label</strong> and <strong>aria-hidden</strong> are supported by all modern browsers and can be used without concern.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Ensuring accessibility in web design is more than just a nice-to-haveāitās a necessity. By implementing simple solutions like combining CSS with <strong>aria-hidden</strong>, you can create a user experience that is both aesthetically pleasing and accessible for everyone, including those who rely on screen readers. While there may be different approaches to solving accessibility challenges, the key is to be mindful of all users' needs. A few small adjustments can make a world of difference, ensuring that your features are truly usable by everyone.</p>
<p>Cheers<br />
Alex</p>
<h1>Resources / Links</h1>
<ul>
<li><a href="https://www.compart.com/en/unicode/U+1F4C6">Unicode Character āTear-Off Calendarā</a></li>
<li><a href="https://www.compart.com/en/unicode/">comport Unicode Website</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label">mdn web docs aria-label</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-hidden">mdn web docs aria-hidden</a></li>
<li><a href="https://www.w3.org/WAI/standards-guidelines/aria/">WAI-ARIA Standard Guidlines</a></li>
<li><a href="https://tailwindcss.com/docs/screen-readers">Tailwind CSS Screen Readers (sr-only)</a></li>
<li><a href="https://ericwbailey.website/published/aria-label-is-a-code-smell">aria-label is a Code Smell</a></li>
<li><a href="https://www.w3.org/TR/using-aria/#firstrule">First Rule of ARIA Use</a></li>
</ul>
The underrated <dl> element
2024-12-26T00:00:00Z
https://htmhell.dev/adventcalendar/2024/26/
by David Luhr<br><p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl">Description List (<code><dl></code>) element</a> is useful for many common visual design patterns, but is unfortunately underutilized.</p>
<p>It was originally intended to group terms with their definitions, but it's also a great fit for other content that has a key/value structure, such as product attributes or cards that have several supporting details.</p>
<p>Developers often mark up these patterns with overused heading or table semantics, or neglect semantics entirely. With the Description List (<code><dl></code>) element and its dedicated Description Term (<code><dt></code>) and Description Definition (<code><dd></code>) elements, we can improve the semantics and accessibility of these design patterns.</p>
<p>The <code><dl></code> has a unique content model:</p>
<ul>
<li>A parent <code><dl></code> containing one or more groups of <code><dt></code> and <code><dd></code> elements</li>
<li>Each term/definition group can have multiple <code><dt></code> (Description Term) elements per <code><dd></code> (Description Definition) element, or multiple definitions per term</li>
<li>The <code><dl></code> can optionally accept a single layer of <code><div></code> to wrap the <code><dt></code> and <code><dd></code> elements, which can be useful for styling</li>
</ul>
<h2 id="examples">Examples</h2>
<p>An initial example would be a simple list of terms and definitions:</p>
<p><img src="https://htmhell.dev/adventcalendar/2024/26/terms-and-definitions.jpg" alt="Example design with bold text terms followed by regular text definitions." /></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dl</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Compression damping<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>Controls the rate a spring compresses when it experiences a force<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Rebound damping<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>Controls the rate a spring returns to it's extended length after compressing<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dl</span><span class="token punctuation">></span></span></span></code></pre>
<p>A common design pattern is "stat callouts", which feature mini cards of small label text above large numeric values. The <code><dl></code> is a great fit for this content:</p>
<p><img src="https://htmhell.dev/adventcalendar/2024/26/stat-callouts.jpg" alt="Example design with 3 groupings of small label text above large, bold number values." /></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dl</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Founded<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>1988<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Frames built<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>8,678<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Race podiums<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>212<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dl</span><span class="token punctuation">></span></span></span></code></pre>
<p>And, a final example of a product listing, which has a list of technical specs:</p>
<p><img src="https://htmhell.dev/adventcalendar/2024/26/product-details.jpg" alt="Example design a large placeholder image next to a product title and a key/value list of product details." /></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Downhill MTB<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dl</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Front travel:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>160mm<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Wheel size:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>27.5"<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Weight:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>15.2 kg<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dl</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="accessibility">Accessibility</h2>
<p>With this markup in place, <a href="https://a11ysupport.io/tech/html/dl_element">common screen readers will convey important semantic and navigational information</a>. In my testing, NVDA on Windows and VoiceOver on MacOS conveyed a list role, the count of list items, your position in the list, and the boundaries of the list. TalkBack on Android only conveyed the term and definition roles of the <code><dt></code> and <code><dd></code> elements, respectively.</p>
<p>If the design doesn't include visible labels, you can at least include them as visually hidden text for assistive technology users. But, I always advocate to visually display them if possible.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>The <code><dl></code> is a versatile element that unfortunately doesn't get much use. In over a decade of coding, I've almost never encountered it in existing codebases. It also doesn't appear anywhere in the top HTML elements lists in <a href="https://almanac.httparchive.org/en/2024/markup#element-diversity">the Web Almanac 2024</a> or <a href="https://www.advancedwebranking.com/seo/html-study">an Advanced Web Ranking study of over 11.3 million pages</a>. The next time you're building out a design, look for opportunities where the underrated Description List is a good fit.</p>
<p>To go deeper, be sure to check out this <a href="https://benmyers.dev/blog/on-the-dl/">article by Ben Myers on the <code><dl></code> element</a>.</p>
Preloading fonts for web performance with link rel="preload"
2024-12-25T00:00:00Z
https://htmhell.dev/adventcalendar/2024/25/
by Alistair Shepherd<br><p>Web performance is incredibly important. If you were here for the advent calendar last year you may have already read many of my thoughts on the subject. If not, read <a href="https://htmhell.dev/adventcalendar/2023/14/">Getting started with Web Performance</a> when youāre done here!</p>
<p>This year Iām back for more web performance, this time focusing on my favourite HTML snippet for <strong>improving the loading performance of web fonts using preloads</strong>. This short HTML snippet added to the <code>head</code> of your page, can make a substantial improvement to both perceived and measured performance.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span><br /><span class="highlight-line"> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preload<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/nova-sans.woff2<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>font<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>font/woff2<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">crossorigin</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>anonymous<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">></span></span></code></pre>
<p>Above we have a <code>link</code> element that instructs the browser to preload the <code>/nova-sans.woff2</code> font. By preloading your critical <em>above-the-fold</em> font we can make a huge impact by reducing potential flashes of unstyled or invisible text and layout shifts caused by font loading, like here in the following video:</p>
<figure style="margin-bottom:2.4rem">
<video controls="" width="904" height="680">
<source src="https://htmhell.dev/adventcalendar/2024/25/no-preload.mp4" type="video/mp4" />
</video>
<figcaption>Recording of a page load illustrating how a font loading late can result in a jarring layout shift</figcaption>
</figure>
<h2 id="how-web-fonts-are-loaded">How web fonts are loaded</h2>
<p>To explain how preloading fonts can make such an impact, letās go through the process of how web fonts are loaded. Font files are downloaded later than you may think, due to a combination of network requests and conservative browser behaviour.</p>
<p>In a standard web page, there will be the main HTML document which will include a CSS file using a <code>link</code> element in the <code>head</code>. If youāre using self-hosted custom fonts youāll have a <code>@font-face</code> rule within your CSS that specifies the font name, the <code>src</code>, and possibly some other font-related properties.</p>
<p>In other CSS rules you specify a <code>font-family</code> so elements use your custom font.</p>
<p>Once our browser encounters our page it:</p>
<ol>
<li>Starts streaming the HTML document, parsing it as it goes</li>
<li>Encounters the <code>link</code> element pointing to our CSS file</li>
<li>Starts downloading that CSS file, blocking the render of the page until itās complete</li>
<li>Parses and applies the contents of that file</li>
<li>Finds the <code>@font-face</code> rule with our font URL</li>
</ol>
<p>Okay letās pause here for a moment ā It may make sense for step 6 to be <em>āStarts downloading our font fileā</em>, however thatās not the case. You see, if a browser downloaded every font within a CSS file when it first encountered them, we could end up loading much more than is needed. We could be specifying fonts for multiple different weights, italics, other character sets/languages, or even multiple different fonts.</p>
<p>If we donāt need all these fonts immediately it would be wasteful to download them all, and doing so may slow down higher priority CSS or JS.</p>
<p>Instead, the browser is more conservative and simply takes note of the font declaration until itās explicitly needed. The browser next:</p>
<ol start="6">
<li>Takes a note of our <code>@font-face</code> declarations and their URLs for later</li>
<li>Finishes processing CSS and starts rendering the page</li>
<li>Discovers a piece of text on the page that needs our font</li>
<li>Finally starts downloading our font now it knows itās needed!</li>
</ol>
<p>So as we can see thereās actually a lot that happens between our HTML file arriving in the browser and our font file being downloaded. This is ideal for lower priority fonts, but for the main or headline font this process can make our custom font appear surprisingly late in the page load. This is what causes the behaviour we saw in the video above, where the page starts rendering but it takes some time before our custom font appears.</p>
<figure style="margin-bottom:2.4rem">
<img src="https://htmhell.dev/adventcalendar/2024/25/no-preload.webp" width="1480" height="886" alt="A waterfall chart with type, url, and time columns. Listed below are assets that are loaded including an html document, CSS and JS files, a couple of images and a font file 'lobster.woff2'. It's all fairly standard and expected but the font doesn't even start loading until after the JS is complete at 2.0s, and doesn't finish until 3.0s" loading="lazy" />
<figcaption>A waterfall graph showing how our custom ālobster.woff2ā font doesnāt start being downloaded until 2 seconds into the page load and isnāt available until after 3 seconds</figcaption>
</figure>
<p>This is an intentional decision by browser makers and spec writers to ensure that pages with lots of fonts arenāt badly impacted by having to load many font files ahead of time. But that doesnāt mean it canāt be optimised!</p>
<h2 id="preloading-our-font-with-a-link">Preloading our font with a link</h2>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span><br /><span class="highlight-line"> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preload<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/nova-sans.woff2<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>font<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>font/woff2<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">crossorigin</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>anonymous<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">></span></span></code></pre>
<p>The purpose of my favourite HTML snippet is to inform the browser that this font file will be needed with high priority, before it even has knowledge of it. Weāre building our page and know more about how our fonts are used ā so we can provide hints to be less conservative! If we start downloading the font as soon as possible then it can be ready ahead of when the browser ārealisesā itās needed.</p>
<p>Looking back at our list above, by adding a preload we move the start of the font download from step 9 to step 2!</p>
<ol>
<li>Starts streaming the HTML document, parsing it as it goes</li>
<li>Encounters our preload and starts downloading our font file in the background</li>
<li>Encounters the <code>link</code> element pointing to our CSS file</li>
<li><em>Continues as above</em></li>
</ol>
<p>Taking a closer look at the snippet, weāre using a <code>link</code> element and <code>rel="preload"</code> to ask the browser to preload a file with the intention of using it early in the page load. Like a CSS file, we provide the URL with the <code>href</code> attribute.</p>
<p>We use <code>as="font"</code> and <code>type="font/woff2"</code> to indicate this is a font file in <code>woff2</code>. For modern browsers <code>woff2</code> is the only format you need as itās universally supported.</p>
<p>Finally thereās <code>crossorigin="anonymous"</code>. This comes from the wonderfully transparent and clear world of Cross Origin Resource Sharing. I jest of course, CORS is anything but transparent and clear!</p>
<p>For fonts you almost always want <code>crossorigin="anonymous"</code> on your link element, <strong>even when the request isnāt cross-origin</strong>. Omitting this attribute would mean our preload wouldnāt be used and the file would be requested again. But why?</p>
<p>Browser requests can be sent either with or without credentials (cookies, etc), and requests to the same URL with and without credentials are fundamentally different. <strong>For a preload to be used by the browser, it needs to match the type of request that the browser would have made normally.</strong> By default fonts are always requested without credentials, so we need to add <code>crossorigin="anonymous"</code> to ensure our preload matches a normal font request.</p>
<p>By omitting this attribute our preload would not be used and the browser would request the font again. If youāre ever unsure of how your preloads are working, check your browsersā devtools. In Chrome the Network pane will show a duplicate request, and the Console will log a warning telling you a preload wasnāt used.</p>
<figure style="margin-bottom:2.4rem">
<img src="https://htmhell.dev/adventcalendar/2024/25/chrome-devtools.webp" width="1480" height="536" alt="Screenshot of Google Chrome devtools 'Console' pane. There are two warnings: 1. 'A preload for 'http://localhost:3000/lobster.woff2' is found, but is not used because the request credentials mode does not match. Consider taking a look at crossorigin attribute.'; 2. 'The resource http://localhost: 3000/lobster.woff2 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally.'" loading="lazy" />
<figcaption>Screenshot showing the Chrome devtools Console pane, with warnings for an incorrect font preload</figcaption>
</figure>
<h2 id="result-and-conclusion">Result and conclusion</h2>
<p>By preloading our critical fonts we ensure our browser has the most important fonts available earlier in the page loading process. We can see this by comparing our recording and waterfall charts from earlier:</p>
<figure style="margin-bottom:2.4rem">
<video controls="" width="1808" height="680">
<source src="https://htmhell.dev/adventcalendar/2024/25/side-by-side.mp4" type="video/mp4" />
</video>
<figcaption>Side-by-side recording of the same page loading in different ways. āno-preloadā shows a large layout shift caused by the font switching and finishes loading at 4.4s. āpreloadā doesnāt have a shift and finishes loading at 3.1s.</figcaption>
</figure>
<figure style="margin-bottom:2.4rem">
<img src="https://htmhell.dev/adventcalendar/2024/25/side-by-side.webp" width="1480" height="440" alt="A side-by-side of two waterfall charts including type, url and time columns. On one side is 'no-preload' and the other is 'preload'. The key difference is that in 'no-preload' a font doesn't start loading until way after everything else at 2.0s. In 'preload' it loads very early, alongside other assets. Because of this the page finishes loading much earlier." loading="lazy" />
<figcaption>Side-by-side comparison of two waterfall charts of the same site with font file `lobster.woff2`. For the āno-preloadā document the font loads after all other assets and finishes at 3s. The āpreloadā document shows the font loading much earlier, in parallel with other files and finishing at 2s.</figcaption>
</figure>
<p>As I mentioned in <a href="https://htmhell.dev/adventcalendar/2023/14/">Getting started with Web Performance</a>, itās best to use preloads sparingly so limit this to your most important font or two. Remember that itās a balance. By preloading too many resources you run the risk of other high-priority resources such as CSS being slowed down and arriving late. I would recommend preloading just the heading font to start with. With some testing you can see if preloading your main body font is worth it also!</p>
<p>With care, font preloads can be a simple and impactful optimisation opportunity and this is why itās my favourite HTML snippet! This is a great step to improving font loading, and there are <a href="https://htmhell.dev/adventcalendar/2023/14/#6-be-bold-optimise-your-fonts">plenty of other web font optimisations</a> to try also!</p>
The search input: They almost got it right
2024-12-24T00:00:00Z
https://htmhell.dev/adventcalendar/2024/24/
by Steve Frenzel<br><p>This example is a classic - in a bad way - and can cause quite some confusion for users of assistive technology (AT). But it's also very easy to fix! It's the <code><input></code> element missing its dear friend, the <code><label></code>... š</p>
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Search<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>It's not relevant for this article, but here's the "button" to submit the content of the <code><input></code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>_icon_1f3oz_44<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">title</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>gl-icon__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>img<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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>gl-icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>use</span> <span class="token attr-name"><span class="token namespace">xlink:</span>href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#search<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>use</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>The cherry on top: It's not even wrapped inside a <code><form></code> element, so it's very likely that the submit is handled via JavaScript. š</p>
<p>So what's the issue with this <code><input></code> element? In theory, having a <code>placeholder</code> instead of a <code><label></code> element is only a temporary solution! Once you've typed something, the placeholder gets replaced with whatever you've typed.</p>
<p>This could be a big issue for screen reader users. Let's check how different screen readers handle this kind of situation with different browsers.</p>
<p>You can do it yourself here: <a href="https://codepen.io/stvfrnzl/pen/jENPqxb">The search input: They almost got it right (bad code example)</a></p>
<p>What I checked was if the placeholder value still gets announced after typing something. I tested with macOS Sequoia 15.1.1 on December 1, 2024 using the latest versions of screen readers and browsers.</p>
<table>
<thead>
<tr>
<th></th>
<th>Google Chrome</th>
<th>Mozilla Firefox</th>
<th>Microsoft Edge</th>
<th>Apple Safari</th>
</tr>
</thead>
<tbody>
<tr>
<td>JAWS</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>n/a</td>
</tr>
<tr>
<td>NVDA</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>n/a</td>
</tr>
<tr>
<td>Narrator</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>n/a</td>
</tr>
<tr>
<td>VoiceOver</td>
<td>No</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr>
</tbody>
</table>
<p>Only VoiceOver had issues announcing announcing the <code>placeholder</code> attribute (except when using Firefox), which might be a bug! This issue has been mentioned back in 2017 for iOS at <a href="https://bugs.webkit.org/show_bug.cgi?id=170334">WebKit Bugzilla</a> and I wonder how long it's been present again on MacOS since then...</p>
<!-- MM: Not so curious to me because that's the expected behavior. The fact that VO doesn't announce it looks like a bug to me. Have you checked the webkit bug tracker? -->
<h2 id="advantages-of-using-lesslabelgreater-instead-of-placeholder">Advantages of using <code><label></code> instead of <code>placeholder</code></h2>
<p>Even though almost all screen readers in this list seem to handle a missing <code><label></code> and a present <code>label</code> attribute well, there are still many reasons why you should do it the other way around:</p>
<h3 id="reduce-cognitive-load">Reduce cognitive load</h3>
<p>Having a form with many inputs, no labels and only placeholders will make it very hard to remember the required information. This lacking clarity can place a huge burden on people having trouble to recall many things at once.</p>
<p>It will also make it harder to check against the requirements of the inputs, as the placeholder acting as a hint has been replaced with what you've typed. Therefore errors will be harder to fix, as you can't easily check against the requirement anymore.</p>
<p>Using a descriptive <code><label></code> can avoid these issues, as it's persistent.</p>
<h3 id="avoid-flaky-browser-and-screen-reader-support">Avoid flaky browser and screen reader support</h3>
<p>Some older browsers might hide the placeholder text once the input is focused. And as you read earlier, some screen readers might have issues with a <code>placeholder</code> instead of a <code><label></code>.</p>
<p>Using a descriptive <code><label></code> can avoid these issues, as it has <a href="https://caniuse.com/mdn-html_elements_label">great browser support</a> and will be <a href="https://a11ysupport.io/tech/html/label_element">recognized by screen readers</a> as well.</p>
<h3 id="increase-touch-target-size">Increase touch target size</h3>
<p>A <code>placeholder</code> is inside the text input. When using a mouse, this will be the only area you can click to focus this input. People with tremors might have a hard time doing that, depending on the size of the inputs and how close they're to each other.</p>
<p>Using a descriptive <code><label></code> can avoid these issues, as clicking it will focus the input.</p>
<p>For more reasons why, check out Adam Silver's article listed under <a href="https://htmhell.dev/adventcalendar/2024/24/#further-reading">Further reading</a>.</p>
<p>Enough of the theory, let's check out some working examples! You can find them here: <a href="https://codepen.io/stvfrnzl/pen/VYZLjLR">The search input: They almost got it right (good code examples)</a></p>
<h2 id="1-good-code-with-explicit-label">1. Good code with explicit label</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Search:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search-input<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<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>search<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>This common approach provides a visual cue for the <code><input></code> element and can be processed by AT.</p>
<p>The <code><input></code> is now of <code>type="search"</code>, which should expose it as "searchbox" in the <a href="https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree">accessibility tree</a>.</p>
<p>Clicking the label will also focus the input. This is a nice helper for sighted users and people with motor disabilities, as the target area for clicking has been increased.</p>
<p>Additionally, you can target this input now with CSS! This way you can check yourself that you implemented semantic and accessible HTML:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">input[type="search"]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* Your code */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p><code>for="search-input"</code> of <code><label></code> is referencing <code>id="search-input"</code> of <code><input></code>, so they're now "connected", if you will. AT should always announce the label of this input, no matter what you've typed.</p>
<p>The <code>name</code> attribute is useful when submitting the form, so you know which <code><input></code> contains what information.</p>
<p>If, for whatever reason, you don't want to show the visual label, you could also do it this way:</p>
<h2 id="2-good-code-using-aria-label">2. Good code using <code>aria-label</code></h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</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>Search<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>search-input<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<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>search<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Search<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>There's no direct connection between <code><input></code> and <code><button></code>, but it is still of <code>type="search"</code> and has the accessible name "Search", which should give enough clues for AT users.</p>
<p>However, the only visual clue here is the button. So make sure it's close to the input, in order for people to make the mental connection. Not a fan of it though, as I would rather connect the two elements with each other:</p>
<h2 id="3-good-code-using-aria-labelledby">3. Good code using <code>aria-labelledby</code></h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search-input<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<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>search<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search-input<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>submit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Search<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Still no visual cue, but whenever we change the <code><button></code> name, the accessible name of the <code><input></code> will change accordingly. The power of the <code>aria-labelledby</code> attribute! šŖ There's one more approach:</p>
<h2 id="4-ok-code-with-implicit-label">4. OK code with implicit label</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Search:</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p>As the <code><input></code> is wrapped inside the <code><label></code> element, they are both connected now. However, AT support for this solution might not be as good as an explicit label (see <a href="https://www.w3.org/WAI/tutorials/forms/labels/#associating-labels-implicitly">Associating labels implicitly</a>).</p>
<h2 id="going-the-extra-mile-using-landmarks">Going the extra mile using landmarks</h2>
<p>To make it even easier for AT users to access the search functionality on your website, you could add <code>role="search"</code> to your <code><form></code> element, or wrap it inside the <code><search></code> <a href="https://developer.mozilla.org/en-US/blog/aria-accessibility-html-landmark-roles/">landmark</a>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</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>search<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Search:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search-input<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<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>search<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>This should announce the container as "search" in the accessibility tree, same goes for this approach:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>search</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Search:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search-input<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<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>search<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>search</span><span class="token punctuation">></span></span></span></code></pre>
<p>When people are traversing the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model">DOM</a> using landmarks, this could help them find the search input on your website much quicker. And even if it's not announced as "search", you'd still have a very descriptive <code><label></code> element and a user input of <code>type="search"</code>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>All in all, this wasn't a very complex or time consuming fix and it will help people a lot to locate and use the search input on your website. Different approaches are possible, like using an implicit or explicit label, or even no visual label at all!</p>
<p>I personally like to use an explicit label, as it's a common pattern AND it's something you could target with CSS. Don't use an <code><input></code> element without a <code><label></code> or <code>aria-label</code>, as it comes with plenty of benefits for you and your users.</p>
<p>And wrapping an input inside a <code><form role="search"></code> or the <code><search></code> landmark will provide an extra hint for them, which could make it even easier to get there. So don't forget to always pair <code><input></code> with a <code><label></code>, they belong together and are the ultimate couple! ā¤ļø</p>
<h2 id="further-reading">Further reading</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/search"><code><input type="search"></code></a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input"><code><input></code>: The HTML Input element</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/search"><code><search></code>: The generic search element</a></li>
<li><a href="https://a11ysupport.io/">Accessibility Support: Will your code work with assistive technologies?</a></li>
<li><a href="https://adamsilver.io/blog/the-problem-with-placeholders-and-what-to-do-instead/">The problem with placeholders and what to do instead</a></li>
</ul>
The devil is in the <details>
2024-12-23T00:00:00Z
https://htmhell.dev/adventcalendar/2024/23/
by J. Pedro Ribeiro<br><p>Not too long ago, building an accordion component would require you to use a combination of JavaScript and CSS. If you've been around for as long as I have, you might have used a library like <a href="https://jqueryui.com/accordion/">jQuery</a> or <em>Mootools</em>.<br />
If you went <em>"vanilla"</em>, your code would look something like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>my-js-accordion<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Learn more about accordions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Accordions encapsulate content under a heading.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.hidden</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<pre class="language-js"><code class="language-js"><span class="highlight-line">document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">".my-js-accordion"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">accordion</span><span class="token punctuation">)</span><span class="token operator">=></span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> accordion<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"span"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span><span class="token operator">=></span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> accordion<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"p"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token string">"hidden"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>And it would work as you expected:</p>
<style>
.my-js-accordion {
font-family: sans-serif;
font-size: 16px;
border: 1px solid #ccc;
background: #efefef;
padding: 8px;
border-radius: 10px;
margin-bottom: 16px;
}
</style>
<style>
.my-js-accordion span {
cursor: pointer;
}
</style>
<style>
.my-js-accordion p {
margin: 8px 0 0;
}
</style>
<style>
.my-js-accordion .hidden {
display: none;
}
</style>
<div class="my-js-accordion ex1-js">
<span>ā Learn more about accordions</span>
<p class="hidden">Accordions encapsulate content under a heading.</p>
</div>
<div class="my-js-accordion ex1-js">
<span>ā This one was built with JS and CSS</span>
<p class="hidden">We needed JavaScript and CSS to make this work. Click the heading again to close it.</p>
</div>
<div class="my-js-accordion ex1-js">
<span>ā Each accordion on this example works independently</span>
<p class="hidden">For any other advanced feature, more JavaScript will be needed.</p>
</div>
<script>
document.querySelectorAll(".ex1-js").forEach((accordion)=>{
accordion.querySelector("span").addEventListener("click", (e)=>{
accordion.querySelector("p").classList.toggle("hidden");
});
});
</script>
<p>The more adventurous would hack together some <code>input</code> and <code>label</code> elements together. With the help of <code>:checked</code> and some clever<br />
CSS selectors, you could achieve the same visual representation as the one above but without the need for JavaScript.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>my-css-accordion<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>accordion1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>accordion1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ā Learn more about accordions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Accordions encapsulate content under a heading.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="token selector">.my-css-accordion input[type="checkbox"],<br />.my-css-accordion p</span> <span class="token punctuation">{</span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.my-css-accordion input[type="checkbox"]:checked + label + p</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And it would be visually the same as the previous example:</p>
<style>
.my-css-accordion input[type="checkbox"],
.my-css-accordion p {
display: none;
}
</style>
<style>
.my-css-accordion input[type="checkbox"]:checked + label + p {
display: block;
}
</style>
<style>
.my-css-accordion label {
cursor: pointer;
font-weight: normal;
}
</style>
<div class="my-css-accordion my-js-accordion">
<input type="checkbox" id="accordion1" />
<label for="accordion1">ā Learn more about accordions</label>
<p>Accordions encapsulate content under a heading.</p>
</div>
<div class="my-css-accordion my-js-accordion">
<input type="checkbox" id="accordion2" />
<label for="accordion2">ā This one was built without JavaScript</label>
<p>Only CSS and HTML!</p>
</div>
<div class="my-css-accordion my-js-accordion">
<input type="checkbox" id="accordion3" />
<label for="accordion3">ā Same looks, same behaviour</label>
<p>However, the code is slightly harder to maintain</p>
</div>
<p>These techniques work but there have some disadvantages: not only they're hard to maintain, they're also not accessible, and their reliance on JavaScript makes them not the ideal candidate for a modern web application.<br />
In summary, another case of <a href="https://www.htmhell.dev/">HTMLHell</a>.</p>
<h2 id="here-comes-a-new-challenger">Here comes a new challenger</h2>
<p>Since 2020, all major browsers <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details#browser_compatibility">support</a> the <code><details></code> and its companion <code><summary></code> elements. Combined, they replace the need for JavaScript and CSS hacks to create an accordion component.<br />
We use the following syntax:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>Your heading goes here<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Any content you like goes below the summary tag.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>As many elements as you need.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<p>And it will work just like the previous examples, but with some added benefits:</p>
<ul>
<li>Less code ā
</li>
<li>Fully accessible ā
</li>
<li>Works without JavaScript ā
</li>
<li>No need for hacks ā
</li>
<li>Fully stylable ā
</li>
</ul>
<p>As seen below:</p>
<style>
.styled {
border: 1px solid #ccc;
background: #efefef;
padding: 8px;
border-radius: 10px;
margin-bottom: 20px;
}</style>
<style>
.styled summary {
font-family: sans-serif;
font-size: 16px;
cursor: pointer;
font-weight: normal;
}
</style>
<style>
.styled p {
font-family: sans-serif;
font-size: 14px;
margin: 8px 0;
}
</style>
<details class="styled">
<summary>Your heading goes here</summary>
<p>Any content you like goes below the summary tag.</p>
<p>As many elements as you need.</p>
</details>
<details class="styled">
<summary>This is the second heading</summary>
<p>Any content you like goes below the summary tag.</p>
<p>As many elements as you need.</p>
</details>
<details class="styled">
<summary>And the third entry it's here</summary>
<p>Any content you like goes below the summary tag.</p>
<p>As many elements as you need.</p>
</details>
<p>On the example above, each <code><details></code> element works independently of each other. But what if you want them to behave like true accordions, where only one can be opened at a time?</p>
<h2 id="one-more-thing-exclusive-accordions">One More Thing... Exclusive Accordions</h2>
<p>Not too long ago, the <code>name</code> attribute was added to the <code>details</code> element.<br />
Similarly to the usage in the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio">radio input</a>, it allows you to group multiple elements together. This means we can have <em>exclusive</em> accordions: when only one can be opened at a time.</p>
<p>Here is an example of the usage:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-accordion-group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-accordion-group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-accordion-group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<p>Give the same <code>name</code> attribute to all the <code>details</code> elements you want to group together and they'll work as expected:</p>
<style>
.styled {
border: 1px solid #ccc;
background: #efefef;
padding: 8px;
border-radius: 10px;
margin-bottom: 20px;
}</style>
<style>
.styled summary {
font-family: sans-serif;
font-size: 16px;
cursor: pointer;
font-weight: normal;
}
</style>
<style>
.styled p {
font-family: sans-serif;
font-size: 14px;
margin: 8px 0;
}
</style>
<details name="group" class="styled">
<summary>3 separate entries</summary>
<p>First block content.</p>
<p>End of first block.</p>
</details>
<details name="group" class="styled">
<summary>Only one can be opened at a time</summary>
<p>Second block content.</p>
<p>End of second block.</p>
</details>
<details name="group" class="styled">
<summary>Still, no JS needed!</summary>
<p>Third block content.</p>
<p>End of third block.</p>
</details>
<p><strong>Note</strong>: It's worth highlighting that exclusive accordion come with some <em>drawbacks</em>, including poor UX on keyboard navigation and reduced usability for screen readers. Eric Eggert wrote about <a href="https://yatil.net/blog/exclusive-accordions">these issues</a>, offering some solutions and insights that are worth reading.</p>
<h2 id="take-it-to-the-next-level">Take it to the next level</h2>
<p>Recently, Chrome 131 <a href="https://developer.chrome.com/blog/styling-details">added support</a> to new ways you can style both <code><details></code> and <code><summary></code> elements. Still early days but the future looks bright!<br />
Combine these features with some creative styling and you can create really complex components with accessibility and scalability built-in.</p>
<p>Further reading and examples:<br />
<a href="https://codepen.io/jpedroribeiro/pen/YzmxNYx">Code snippets</a> | <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details">MDN</a> | <a href="https://caniuse.com/details">Can I use</a> | <a href="https://web.dev/learn/html/details">web.dev</a></p>
PSA: Stop using the title attribute as tooltip!
2024-12-22T00:00:00Z
https://htmhell.dev/adventcalendar/2024/22/
by Daniela Kubesch<br><p>It's almost 2025, so it's time to stop using the <code>title</code> attribute everywhere. Images, text, buttons, ... you name it, devs really like to put it on any element in sight. Most of the time, people actually want to create a tooltip. You know, that little bubble of information designed to clarify the purpose of otherwise unclear elements, that pops up attached to an element when its receives focus or a user hovers their mouse over it.</p>
<p>The identifying thing about tooltips is that they contain no interactive elements (aka. only plain text), and are always attached to existing interactive elements.<br />
Whenever you want to add interactive elements inside your information bubble, it's not called <em>tooltip</em>, but <em>toggletip</em>. Toggletips can contain semantic markup, rich content and interactive elements, and usually only appear when an element is clicked. The great thing about toggletips is that they're accessible on touchscreens and easier to find and recognise for users with low vision.</p>
<p>So depending on your use case, you need to implement a tooltip or a toggletip.</p>
<p>But let's circle back to the <code>title</code> attribute. Often, people use it to create a tooltip. However, this is not the recommended way to go.</p>
<h2 id="but-why-i-love-my-title!?">But why, I love my <code>title</code>!?</h2>
<p>It's simple. The <code>title</code> attribute is inaccessible. Users of mobile phones and tablets, users of assistive technologies, and keyboard only users cannot interact with it.<br />
If you want a tooltip, a much better, and accessible, option is using the <code>popover</code> attribute.</p>
<p><strong>Note:</strong> The only place where you should (& must) use the <code>title</code> attribute is on an <code>iframe</code>! See <a href="https://html5accessibility.com/stuff/2021/08/26/named-and-framed/">Steve Faulkner's post</a> for more information.</p>
<h2 id="how-to-use-popover">How to use <code>popover</code></h2>
<p>Firstly, before we get started, it is always better to display clear, permanently visible information. So, if space permits, do not use tooltips. Instead, provide clear labels and sufficient text. This is particularly important for forms!</p>
<p>However, if you wanna go down that path, the <code>popover</code> attribute provides a starting point for building popover-like interactions on the web. Its purpose is simply to add 'popover/tooltip behaviour'. So we'll use it to create our custom plain-text tooltip.</p>
<p>To start, we just need an interactive element (like a button) which is used to trigger the tooltip, even when navigating with a keyboard only, and a <code><div></code> containing the tooltip content.</p>
<p>The <code><button></code> is linked to the tooltip with <code>aria-describedby</code>.<br />
The <code><div></code> receives the <code>popover</code> attribute and an <code>id</code>.<br />
As the <code>popover</code> attribute just adds behaviour, not semantics, we need to <a href="https://hidde.blog/popover-semantics/">add our own role when it makes sense</a>. Therefore we add <code>role='tooltip'</code> to the <code><div></code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> There is a</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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>tooltip<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>secret<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span> to accessible</span><br /><span class="highlight-line"> HTML!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">popover</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>tooltip<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>tooltip<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>a div is not a button āØ<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="lets-make-it-interactive">Let's make it interactive</h3>
<p>If we would want to create a <em>toggletip</em>, which opens <code>onClick</code>, we could simply add <code>popovertarget="ID"</code> to the <code><button></code>, with the <code>id</code> of the toggletip.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">There is a </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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>toggletip<span class="token punctuation">"</span></span> <span class="token attr-name">popovertarget</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>toggletip<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> secret</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">to accessible HTML!</span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">popover</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>toggletip<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>a div is not a button āØ<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<div class="demo-toggletip">
<p>There is a <button type="button" class="popoverbutton togglebutton" aria-describedby="toggletip" popovertarget="toggletip">secret</button> to accessible HTML!</p>
<div popover="" id="toggletip">
<div class="tooltip-content">a div is not a button āØ</div>
</div>
</div>
<p>However, if we want our <em>tooltip</em>, that is triggered when the interactive element is hovered or focused, we need JavaScript.<br />
We can display the tooltip by using <code>.hidePopover()</code> and <code>.showPopover()</code>.<br />
If <code>showPopover()</code> is called on an element with the popover attribute that is currently hidden, the element is added to the top rendering layer.</p>
<p>A tooltip usually disappears when hitting the Escape key or when the mouse is moved away from the interactive element.<br />
But it's also important to make sure that the tooltip content is reachable with the mouse pointer. That way, users can copy the text or read it with magnification software.<br />
That's why, with the help of JavaScript, we show the tooltip on <code>mouseover</code> of the interactive element and keep it visible when the tooltip itself is hovered (by also listening to <code>mouseover</code>).<br />
With <code>mouseout</code> we can hide the tooltip as soon as the mouse leaves either the tooltip or the interactive element that triggered it.</p>
<p>To open or close the tooltip with keyboard navigation, we must listen to the <code>focusin</code> and <code>focusout</code> events of the button.<br />
The tooltip must be easy to dismiss (e.g. by pressing the Escape key).<br />
It's also worth noting that tooltips don't actually get the focus themselves. The focus stays on the element that triggered the tooltip.<br />
However, it's content is still read to screen reader users and is accessible by the screen readers virtual cursor.<br />
But remember, in general, if we're showing content automatically when an element receives focus, it's important not to suddenly change context and confuse users.</p>
<h4>Wait, is this new attribute supported?</h4>
<p>Good news! The <code>popover</code> attribute is <a href="https://caniuse.com/?search=popover">supported by all modern browsers</a> with versions released between mid-2023 and 2024 (starting with Safari 17.0, Firefox 125, Chrome 114 and Edge 114).</p>
<h3 id="looks-bad-though">Looks bad though</h3>
<p>That's why the last thing to do is use CSS to style the tooltip.<br />
If you want to change how the tooltip looks when it's displayed, you can use the <code>:popover-open</code> pseudo-class.</p>
<p>We also need to make sure the tooltip is correctly positioned.<br />
We can use <code>position-anchor</code> and <code>position-area</code> for Chrome and Edge, but they're still in the experimental phase, so we need an alternative for other browsers. The simplest option would be to use a library like <a href="https://floating-ui.com/">Floating UI</a>.</p>
<style>
html {
--anchor-name: --tooltip;
}
.popoverbutton {
font-size: 20px;
font-family: sans-serif;
font-weight: 600;
}
.demo-toggletip {
--anchor-name: --toggletip;
}
.popoverbutton {
all: unset;
padding: 0;
margin: 0;
background: none;
border: none;
border-bottom: 1px dashed #000;
anchor-name: var(--anchor-name);
font-weight: bold;
}
.popoverbutton:hover {
background: transparent;
}
[popover] {
overflow: visible;
padding: 0;
margin: 0;
border: none;
background: none;
position-anchor: var(--anchor-name);
position-area: var(--popover-inset-area, block-start);
opacity: 0;
}
[popover]:popover-open {
opacity: 1;
}
.tooltip-content {
position: relative;
text-align: center;
line-height: 1.2;
max-inline-size: max-content;
background-color: #0a0a0a;
color: #f2f2f2;
border: 2px solid #f2f2f2;
border-radius: 0.4rem;
padding: 0.5rem 0.75rem;
font-size: 16px;
font-weight: 400;
margin-bottom: 0.25rem;
}
.tooltip-content:after {
font-size: 20px;
content: "";
position: absolute;
bottom: -25%;
left: 45%;
transform: rotate(180deg);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
width: 1.25rem;
height: 0.75rem;
background-color: #0a0a0a;
}
</style>
<p>
There is a
<!-- interactive element triggering the tooltip -->
<button type="button" aria-describedby="tooltip" class="popoverbutton js-button">
secret
</button> to accessible HTML!
</p>
<!-- The custom tooltip -->
<div popover="" role="tooltip" id="tooltip" class="js-content">
<div class="tooltip-content">
a div is not a button āØ
</div>
</div>
<script>
const tooltip = document.querySelector('.js-content');
const tooltipTrigger = document.querySelector('.js-button');
const openTooltip = () => {
tooltip.showPopover()
};
const closeTooltip = () => {
tooltip.hidePopover()
};
tooltipTrigger.addEventListener('mouseover', openTooltip);
tooltip.addEventListener('mouseover', openTooltip);
tooltipTrigger.addEventListener('mouseout', closeTooltip);
tooltip.addEventListener('mouseout', closeTooltip);
tooltipTrigger.addEventListener('focusin', openTooltip);
tooltipTrigger.addEventListener('focusout', closeTooltip);
</script>
<p>And that's it, you just created a custom tooltip!<br />
Find the detailed code in this <a href="https://codepen.io/dnikub/pen/PwYqwJE">CodePen</a>.</p>
Grouping form fields
2024-12-21T00:00:00Z
https://htmhell.dev/adventcalendar/2024/21/
by Matthias Kittsteiner<br><p>When I first stumbled upon <code>fieldset</code> and <code>legend</code>, I didnāt know much about HTML and especially not about accessibility. Everything I noticed was the special way a <code>legend</code> is displayed inside a <code>fieldset</code> ā or rather: alongside the border of a <code>fieldset</code>.</p>
<p>Fast forward to (kind of) today: while working on a contact form, I first could get my hands on this element and learned more about it.</p>
<h2 id="what-is-a-fieldset?">What is a <code>fieldset</code>?</h2>
<p>Every so often Iām surprised how well chosen names in HTML are. <code>fieldset</code> is no exception here. Itās basically a list of form fields (inputs, selects, textareas). It groups all fields inside of it, and makes it a set of fields. And the <code>legend</code> is used as caption of the <code>fieldset</code> (and ultimately it becomes a caption for all of the grouped form fields).</p>
<p>A simple example of a <code>fieldset</code> looks like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>Date<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>date-month<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Month<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>date[month]<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>number<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>date-month<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>date-day<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Day<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>date[day]<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>number<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>date-day<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>date-year<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Year<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>date[year]<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>number<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>date-year<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span></span></code></pre>
<p>Which results in this:</p>
<p><img src="https://htmhell.dev/adventcalendar/2024/21/fieldset-example.png" alt="A fieldset with the group name āDateā and three fields for month, day and year" /></p>
<p>In the image you see the default design for a <code>fieldset</code>: It has a border, that is partly interrupted by the <code>legend</code>. Something, that is not the easiest to achieve with other elements with CSS only. However, in the wild, you wonāt often see it like this.</p>
<h2 id="why-does-it-matter?">Why does it matter?</h2>
<p>While for users capable of browsing websites visually, a <code>fieldset</code> often stays hidden, since the default styling usually is changed. Only the <code>legend</code> is visible as caption for a group of fields. For screen reader users, itās a whole different story if they use it to navigate through the content via keyboard. For every field inside the <code>fieldset</code>, the screen reader announces that it is part of a group as well as the caption. It makes sure that the user is always aware of the context of the group and that the current active field is part of a specific group.</p>
<p>VoiceOver for example will announce the <code>fieldset</code> as āDate, groupā:</p>
<p><img src="https://htmhell.dev/adventcalendar/2024/21/fieldset-voiceover-group.png" alt="VoiceOver announcing a fieldset as āDate, groupā" /></p>
<p>When navigating to the first form field āmonthā, VoiceOver will announce it as āMonth, incrementable edit text number field, Date, Date, groupā</p>
<p><img src="https://htmhell.dev/adventcalendar/2024/21/fieldset-voiceover-group-input.png" alt="VoiceOver announcing a field āmonthā inside a fieldset as āMonth, incrementable edit text number field, Date, Date, groupā" /></p>
<p>Additionally, by using the <code>disabled</code> attribute, you can easily disable a whole group of fields within the <code>fieldset</code>. Feels a little bit like magic. Such disabled fields are neither editable (they ignore user inputs completely), nor will they be submit in a form.</p>
<p>One note though: think about it first before using it. That is also true for a <code>fieldset</code> as a whole.</p>
<p>Ensure fields inside a <code>fieldset</code> are actually connected to each other and even require an identical context. Otherwise, it just makes the announcement more verbose as it should be, since the value of the <code>legend</code> will always be announced to screen reader users (in case of VoiceOver users often even twice). Use it only if the connected fields are not self-explaining if they are used alone, if you canāt properly edit a field the right way without knowing about the context of the group. While connecting multiple fields to enter a date with month/day/year actually will tell you (by a properly set <code>legend</code>) that youāre about to fill out a date, connecting two fields for first and last name wonāt have any positive effect, since they can be filled correctly by their own without knowing that the other field exists.</p>
<h2 id="usage-examples">Usage examples</h2>
<p>Having multiple choice questions, e.g. in a survey, is an ideal example in grouping form fields. You can use the <code>legend</code> as question and radio buttons as the answers:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>When did you hear about fieldsets the first time?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>before-2000<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>eyes-opened<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>before 2000<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>before-2000<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Before 2000<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>between-2000-2009<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>eyes-opened<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2000 ā 2009<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>between-2000-2009<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Between 2000 and 2009<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>between-2010-2019<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>eyes-opened<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2010 ā 2019<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>between-2010-2019<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Between 2010 and 2019<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>today<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>eyes-opened<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2020 ā today<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>today<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Between 2020 and today<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span></span></code></pre>
<p>It could also be used to connect fields for a day, a month and a year for individual input fields to select a specific date (as seen in the first example), as well as hours and minutes to select a time (if native date and time pickers aren't an option). Or you could have different fields for a credit card number by splitting them into four digits each. You see, there are many possibilities to use <code>fieldset</code> elements, just donāt overuse them ā because with many possibilities comes great responsibility ā or so.</p>
<p>Make also sure to always test your <code>fieldset</code> elements with a screen reader to see whether adding a context in such a way is useful. This will make it easier to distinguish whether you should use a <code>fieldset</code> or better use non-connected single form fields.</p>
My favourite colour is Chuck Norris red
2024-12-20T00:00:00Z
https://htmhell.dev/adventcalendar/2024/20/
by Declan Chidlow<br><p>Setting the colour of text on a webpage is usually a simple affair involving whipping it out the good ol' CSS <code>color</code> property. But this is HTMHell, dammit. None of that wishy-washy CSS nonsense here. No siree. We use HTML as the good lord intended and shalln't stray into the sins of cascading sheets lest we end up some non-HTML variant of hell where they define page structure with JavaScript vars.</p>
<p>But HTML isn't great for defining styles -- or at least, it isn't anymore. If we wind back the clocks a few years to HTML versions of old, we find the colour attribute. If you've been around for a while, you've no doubt seen it. Something like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>font</span> <span class="token attr-name">color</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#d72b2b<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HTMHell rules!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>font</span><span class="token punctuation">></span></span></span></code></pre>
<p><font color="#d72b2b">HTMHell rules!</font></p>
<p>If we render that in a browser, we get some text in the lovely HTMHell red. That's great. That's what we'd expect. Next we'll choose another colour. Something a bit different. Let's try 'chucknorris'.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>font</span> <span class="token attr-name">color</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>chucknorris<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>But... Chuck Norris isn't a colour.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>font</span><span class="token punctuation">></span></span></span></code></pre>
<p><font color="chucknorris">But... Chuck Norris isn't a colour.</font></p>
<p>If you go through the effort of loading <em>that</em> up in a browser, you might notice it makes the text red. Why?</p>
<h2 id="some-funny-character-parsing">Some funny character parsing</h2>
<p>HTML generally doesn't have an error state, at least not one akin to what would happen if writing something like invalid JavaScript. Browsers are very forgiving when parsing HTML (which explains how people have gotten away with the crimes documented throughout this website) and generally do their best to make up for user error. If you leave a dangling <code><div></code>, the browser will do its best to close it up and render it out.</p>
<p>This forgiveness is the reason behind the funkiness. Browsers simply try to forge ahead with the invalid value and hope it'll work. In the past web browsers all handled invalid values a bit differently, but now it's all outlined in the <a href="https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-a-legacy-colour-value">"rules for parsing a legacy color value" part of the HTML spec</a>. A surmised version of the parsing outlined there is as follows:</p>
<ol>
<li>
<p>Initial Cleanup:</p>
<ul>
<li>If an octothorpe (#) is located at the start of the value, it's removed.</li>
<li>The colour attribute only accepts hexes, so there isn't a point keeping it.</li>
<li>Example: "#FF0000" becomes "FF0000".</li>
</ul>
</li>
<li>
<p>Replace Invalid Characters:</p>
<ul>
<li>Any non-hexadecimal characters (anything not 0-9 or A-F/a-f) are removed and replaced with '0'.</li>
<li>Example: 'abcxyz123' becomes 'abc000123'.</li>
</ul>
</li>
<li>
<p>Standardise Length:</p>
<ul>
<li>While the string's length is 0 or not divisible by 3, append '0'.</li>
<li>Examples:
<ul>
<li>"F" becomes "F00" (padded to length 3).</li>
<li>"FFFF" becomes "FFFF00" (padded to length 6).</li>
<li>"FFFFFF0" becomes "FFFFFF000" (padded to length 9).</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Split into Red, Green, and Blue:</p>
<ul>
<li>The first third becomes the red value.</li>
<li>The second third becomes the green value.</li>
<li>The last third becomes the blue value.</li>
<li>Example: "FFFFFF000" becomes ["FFF", "FFF", "000"].</li>
</ul>
</li>
<li>
<p>Handle Length:</p>
<ul>
<li>If any component is longer than 8 characters, remove the characters from the left until it's 8 characters long.
<ul>
<li>Example: "123456789" ā "23456789"</li>
</ul>
</li>
<li>While the length is greater than 2, and all components start with '0', remove the leading '0' from each component.
<ul>
<li>Example: ["000F", "000F", "000F"] becomes ["00F", "00F", "00F"] which then becomes ["0F", "0F", "0F"].</li>
</ul>
</li>
<li>If length is still greater then 2 keep only the first 2 characters of each component.
<ul>
<li>Example: ["ABC", "DEF", "123"] becomes ["AB", "DE", "12"].</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Putting It Together:</p>
<ul>
<li>Get the final red, blue, and green components, then put them together in that order to create the colour.</li>
<li>Example: ["AB", "DE", "12"] becomes ABDE12.</li>
</ul>
</li>
</ol>
<p>I've written a small tool over on CodePen that will take any inputted value, break down the processing step by step, and output the colour as it would be handled. Go have a bit of fiddle!</p>
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="yLmKBpN" data-pen-title="Legacy HTML Colour Parsing Demo" data-user="OuterVale" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/OuterVale/pen/yLmKBpN">
Legacy HTML Colour Parsing Demo</a> by Declan Chidlow (<a href="https://codepen.io/OuterVale">@OuterVale</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2 id="some-fun-examples">Some fun examples</h2>
<p>So, we know this happens and why. The next task is obviously to have some fun with it. Finding words whose computed colours correlate with them is great fun. For example, 'Sonic' gives us a lovely blue like the hedgehog. I've put together a little table of some of these coincidental match ups:</p>
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="wvLbjpZ" data-pen-title="Word Correlations With HTML Colour Parsing" data-user="OuterVale" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/OuterVale/pen/wvLbjpZ">
Word Correlations With HTML Colour Parsing</a> by Declan Chidlow (<a href="https://codepen.io/OuterVale">@OuterVale</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2 id="interesting-parsing-in-the-modern-era">Interesting parsing in the modern era</h2>
<p>So, that's all well and good, but it's old news. The <code>color</code> and <code>bgcolor</code> attributes that permitted our parsing adventures are relics of HTML 4. They're obsolete (though still in active use on a disturbingly high number of websites). That isn't to say quirks like that have disappeared completely though. CSS has its own set of fascinating peculiarities when it comes to handling invalid colour values. Most modern browsers will clamp values rather than reject them outright -ā throw rgb(300, -50, 1000) at a browser and it won't fail; it'll helpfully transform it into rgb(255, 0, 255).</p>
<p>The web's foundational principle of forgiveness -ā the inherent flexibility that allows "chucknorris" to be parsed as red, even though the reason it does so is old, silly, and unsupported ā- hasn't gone anywhere. Modern browsers still bend over backward to make our code work, even when we throw nonsense at them. It doesn't take long to see this forgiveness in action within the cursed examples held within the pages of HTMHell. Each horrifying snippet, each questionable hack, each "it works but why" moment exists because browsers simply refuse to give up on rendering our 'mistakes'.</p>
<p>The web is built on this foundation of resilience, both in technology and <a href="https://www.w3.org/blog/2022/a-letter-from-our-ceo-the-web-as-the-ultimate-tool-of-resilience-for-the-world">ethos</a>. It's what allows a website from 1996 to still render in a modern browser. It's what lets a page load even when half the CSS is invalid. It's what makes it magic.</p>
<p>I've heard people quip that browsers should be less forgiving and enforce perfection. That allowing jank makes the web somehow 'bad'. I think a perfect web would be a boring web. I certainly wouldn't be here writing were it 'perfect'. It's about making the web work, no matter what we throw at it, and I wouldn't have it any other way.</p>
<p>After all, in a perfect web, "chucknorris" would just be another error message -ā and where's the fun in that?</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="http://scrappy-do.blogspot.com/2004/08/little-rant-about-microsoft-internet.html">Sam's Place - A little rant about Microsoft Internet Explorer's color parsing</a></li>
<li><a href="https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-a-legacy-colour-value">HTML Standard</a></li>
<li><a href="https://stackoverflow.com/q/8318911">Why does HTML think "chucknorris" is a color?</a></li>
</ul>
Getting Oriented with HTML Video
2024-12-19T00:00:00Z
https://htmhell.dev/adventcalendar/2024/19/
by Scott Jehl<br><p>A couple years back, I was in a window seat on a flight from Amsterdam to New York. The weather was gray and drizzly as the plane took off, but as it punched through the clouds a very different scene revealed itself. Out my window, it looked like a <a href="https://en.wikipedia.org/wiki/Maxfield_Parrish">Maxfield Parrish</a> painting brought to life. And the plane's speed made it appear to scroll by with this uncanny effect, like a parallax effect that used the wrong easing function. Mesmerizing! I pulled out my phone and recorded a couple of videos.</p>
<p>One in landscape:</p>
<video controls="" playsinline="" muted="" loop="" class="u-mb">
<source src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-1080.mp4" />
</video>
<p>And one in portrait:</p>
<video controls="" playsinline="" muted="" loop="">
<source src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-1080.mp4" />
</video>
<p>I didn't realize at the time that those video orientations would come in handy for a little post on this website.</p>
<p>Let's jump ahead a year to mid 2023. For years, many of us had been advocating for a simple, HTML-based way to serve video files in different sizes and formats using media queriesāessentially how the <code>picture</code> element handles images. I won't rehash the entire history here, but that concept wasn't new or mine to claim. In fact, we once had exactly such a feature. It was a web standard, supported by all browsers, and it even inspired the design of the <code>picture</code> elementānot the other way around. However, just as <code>picture</code> was on track to land, the <code>video</code> features that inspired it were removed from the HTML spec and supporting browsers (except WebKit). Many of us believed this was a mistake, as it left developers without an HTML-only solution to serve responsive, art-directed video content. Finally, sometime mid 2023, representatives from Chrome and Firefox agreed and expressed interest in reintroducing the feature, and "responsive video" was back on the standards track.</p>
<p>Jump forward to today: responsive video is once again part of the HTML standard and built back into browsers as well (<a href="https://scottjehl.com/posts/responsive-video/">I was able to contribute the commits to Firefox</a>)! Now that it's back, we can use <code>media</code> attributes on HTML <code>source</code> elements within HTML <code>video</code>, which allow us to describe the conditions in which a particular version or format of a static video file should be downloaded and displayed. It's not meant to solve every video use case, but it's nice for many of them; particularly if you just want to serve an appropriate video file and can't be bothered (or aren't able) to set up HLS streaming.</p>
<p>In today's post, I want to show you how to use this feature to deliver my airplane video so that it's optimized for any viewport it happens to land on.</p>
<p>Let's start with the HTML: a <code>video</code> element with some <code>source</code>s. Below, I'm offering one video orientation or the other using media queries (check those <code>media</code> attributes on the <code>source</code> elements)!</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>video</span> <span class="token attr-name">autoplay</span> <span class="token attr-name">controls</span> <span class="token attr-name">playsinline</span> <span class="token attr-name">muted</span> <span class="token attr-name">loop</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(orientation: landscape)<span class="token punctuation">"</span></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>sunset-landscape-1080.mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</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>sunset-portrait-1080.mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>video</span><span class="token punctuation">></span></span></span></code></pre>
<p>Nice. We can do better, though. A 1080px-wide video is a large file to send smaller screens and mp4 isn't the smallest format we can offer for a high-quality video of any resolution. Let's use more media queries to offer some smaller video sizes for each orientation and a webm format for each source too, which I put first so that supporting browsers will pick it over the heavier mp4 alternative.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>video</span> <span class="token attr-name">autoplay</span> <span class="token attr-name">controls</span> <span class="token attr-name">playsinline</span> <span class="token attr-name">muted</span> <span class="token attr-name">loop</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width:721px) and (orientation: landscape)<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>video/webm<span class="token punctuation">"</span></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>sunset-landscape-1080.webm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width:721px) and (orientation: landscape)<span class="token punctuation">"</span></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>sunset-landscape-1080.mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width:721px) and (orientation: portrait)<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>video/webm<span class="token punctuation">"</span></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>sunset-portrait-1080.webm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width:721px) and (orientation: portrait)<span class="token punctuation">"</span></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>sunset-portrait-1080.mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width:481px) and (orientation: landscape)<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>video/webm<span class="token punctuation">"</span></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>sunset-landscape-720.webm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width:481px) and (orientation: landscape)<span class="token punctuation">"</span></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>sunset-landscape-720.mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width:481px) and (orientation: portrait)<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>video/webm<span class="token punctuation">"</span></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>sunset-portrait-720.webm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width:481px) and (orientation: portrait)<span class="token punctuation">"</span></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>sunset-portrait-720.mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(orientation: landscape)<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>video/webm<span class="token punctuation">"</span></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>sunset-landscape-480.webm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(orientation: landscape)<span class="token punctuation">"</span></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>sunset-landscape-480.mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(orientation: portrait)<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>video/webm<span class="token punctuation">"</span></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>sunset-portrait-480.webm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(orientation: portrait)<span class="token punctuation">"</span></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>sunset-portrait-480.mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>video</span><span class="token punctuation">></span></span></span></code></pre>
<p>Nice! That's a hefty chunk of HTML to be fair, but it does demonstrate the power we're afforded through these features. The video below uses that exact markup, so depending on the size and shape of your viewport when you load this page, you'll be delivered an appropriate video:</p>
<video controls="" playsinline="" muted="" loop="">
<source media="(min-width:721px) and (orientation: landscape)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-1080.webm" />
<source media="(min-width:721px) and (orientation: landscape)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-1080.mp4" />
<source media="(min-width:721px) and (orientation: portrait)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-1080.webm" />
<source media="(min-width:721px) and (orientation: portrait)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-1080.mp4" />
<source media="(min-width:481px) and (orientation: landscape)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-720.webm" />
<source media="(min-width:481px) and (orientation: landscape)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-720.mp4" />
<source media="(min-width:481px) and (orientation: portrait)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-720.webm" />
<source media="(min-width:481px) and (orientation: portrait)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-720.mp4" />
<source media="(orientation: landscape)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-480.webm" />
<source media="(orientation: landscape)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-480.mp4" />
<source media="(orientation: portrait)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-480.webm" />
<source media="(orientation: portrait)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-480.mp4" />
</video>
<p>It's worth reiterating a caveat here: responsive video works as you'd expect when you first load the page, but it does not reevaluate sources again after that, so you won't see sources adapt as you resize your browser for example. This is intentional: unfortunately, this first pass of reinstated responsive video support needed to match the behavior it formerly had, which only selected an appropriate video source at page load time. As far as I'm concerned, this is a bug that should be addressed. <a href="https://scottjehl.com/posts/rwd-video/">I've written about it and a few others here</a> if you're interested.</p>
<p>Anyway, for now the native implementation falls a little short, but it handles the hard parts and we can make it a little better if we want to anyway!</p>
<p>Below, I'm wrapping the video in <a href="https://scottjehl.com/posts/even-responsiver-video/">a little HTML Web Component I made called <code>responsive-video</code></a>, which will cause the browser to reevaluate its sources when media conditions change, and also keep the video timecode sync'd properly should a source video change. Markup-wise, it works like this, by wrapping the video and referencing a script:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>responsive-video</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>video</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...sources go here</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>video</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>responsive-video</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>module<span class="token punctuation">"</span></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>responsivevideo.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></span></code></pre>
<p>And here it is in action!</p>
<responsive-video>
<video controls="" playsinline="" muted="" loop="">
<source media="(min-width:721px) and (orientation: landscape)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-1080.webm" />
<source media="(min-width:721px) and (orientation: landscape)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-1080.mp4" />
<source media="(min-width:721px) and (orientation: portrait)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-1080.webm" />
<source media="(min-width:721px) and (orientation: portrait)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-1080.mp4" />
<source media="(min-width:481px) and (orientation: landscape)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-720.webm" />
<source media="(min-width:481px) and (orientation: landscape)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-720.mp4" />
<source media="(min-width:481px) and (orientation: portrait)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-720.webm" />
<source media="(min-width:481px) and (orientation: portrait)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-720.mp4" />
<source media="(orientation: landscape)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-480.webm" />
<source media="(orientation: landscape)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-landscape-480.mp4" />
<source media="(orientation: portrait)" type="video/webm" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-480.webm" />
<source media="(orientation: portrait)" src="https://htmhell.dev/adventcalendar/2024/19/sunset-portrait-480.mp4" />
</video>
</responsive-video>
<script type="module" src="https://htmhell.dev/adventcalendar/2024/19/responsivevideo.js"></script>
<p>With that little helper component, our video adapts its size and orientation not only at load, but continuously through a session. <em>Somebody tell YouTube Shorts and TikTok!</em></p>
<p>So now you know how to serve a video in portrait or landscape orientation declaratively with HTML. Happy Holidays.</p>
<script>
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
const videos = document.querySelectorAll('video');
if (!mediaQuery.matches) {
videos[0].setAttribute('autoplay', 'autplay')
}
</script>
Microdata for books
2024-12-18T00:00:00Z
https://htmhell.dev/adventcalendar/2024/18/
by Alan Dalton<br><h2 id="dive-into-marking-up-books">Dive into marking up books</h2>
<p>Books are the best Christmas presents, especially for us web geeks. (I hope youāll find a <a href="https://accessibility-cookbook.com/">Web Accessibility Cookbook</a> in your Christmas stocking, gentle reader.) Unfortunately, <a href="https://abookapart.com/pages/about/">A Book Apart closed</a> this year. Fortunately, the authors reacquired the rights to their books.</p>
<p>To track the authorsā preferred ways of making their books available, I created an <a href="https://alandalton.github.io/Authors-Apart/">āAuthors Apartā webpage</a>. For the HTML, I recalled the <a href="https://diveinto.html5doctor.com/extensibility.html">āāDistributed,ā āExtensibility,ā & Other Fancy Wordsā chapter</a> of Mark Pilgrimās seminal <a href="https://diveinto.html5doctor.com/">Dive Into HTML5</a>, which explains <a href="https://html.spec.whatwg.org/multipage/microdata.html#microdata">HTMLās microdata features</a>. Mark wrote that article 13 years ago with a focus on Google Rich Snippets, and so I also referred to his <a href="https://web.archive.org/web/20110607011745/http://diveintomark.org/archives/2011/06/02/schema-org">unofficial guide to migrating Google Rich Snippets to schema.org</a> and the more recent <a href="https://schema.org/Book">āBookā schema from Schema.org</a>. Armed with that knowledge, hereās how I structured the HTML for each book to convey the name, URL, authorās name, authorās URL, original publishing date, date when the book was updated (if any), and whether itās available for free:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name">itemscope</span> <span class="token attr-name">itemtype</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://schema.org/Book<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">itemprop</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url<span class="token punctuation">"</span></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>https://html5forwebdesigners.com/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>abbr</span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>HyperText Markup Language<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HTML<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>abbr</span><span class="token punctuation">></span></span>5 for Web Designers<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>author<span class="token punctuation">"</span></span> <span class="token attr-name">itemscope</span> <span class="token attr-name">itemtype</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://schema.org/Person<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">itemprop</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url<span class="token punctuation">"</span></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>https://adactio.com/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Jeremy Keith<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>author<span class="token punctuation">"</span></span> <span class="token attr-name">itemscope</span> <span class="token attr-name">itemtype</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://schema.org/Person<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">itemprop</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>url<span class="token punctuation">"</span></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>https://x.com/rachelandrew/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Rachel Andrew<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>datePublished<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2010<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span>, </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dateModified<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2016<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">itemprop</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>isAccessibleForFree<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>š<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="why-i-like-this-html">Why I like this HTML</h2>
<p>I like this HTML because it conveys so much programmatically determinable information ā check out the <a href="https://validator.schema.org/#url=https%3A%2F%2Falandalton.github.io%2FAuthors-Apart%2F">structured data</a>! ā using only 13 elements, and without compromising accessibility. In 2022ās <a href="https://www.htmhell.dev/adventcalendar/2022/17/">Modern HTML as a foundation for progressive enhancement</a>, GaĆ«l Poupard asked, āWhat if we could improve the HTML stack [ā¦], making the markup step more resilient?ā I consider microdata, like <a href="https://www.w3.org/WAI/standards-guidelines/aria/">WAI-Aria</a>, a valuable enhancement to HTML.</p>
<p>Markās article asked the reader to pay attention to each word in this sentence: āMicrodata annotates the DOM with scoped name/value pairs from custom vocabularies.ā The annotation is the enhancement: microdata wonāt interfere with your usual best practices for HTML, and ā as Doug Abrams explained in <a href="https://www.tpgi.com/mind-the-remediation-gap/">Mind The (Remediation) Gap</a> ā āThe vast majority of accessibility lives in the markup.ā Getting the name/value pairs right can involve some trial and error, but Markās book includes a helpful table that shows <a href="https://diveinto.html5doctor.com/extensibility.html#property-values">where microdata property values come from</a>. You can probably spot those pairs in the HTML above. For example, <code>itemprop="url"</code> and <code>href="https://html5forwebdesigners.com/"</code> specify the URL for the HTML5 for Web Designers book, inside the <code>a</code> element. Notice how nicely this microdata plays with the existing HTML: I was also able to include an <code>abbr</code> element to explain the HTML abbreviation. For whatever content your webpage includes, you can probably find a useful schema in the surprisingly comprehensive <a href="https://schema.org/docs/full.html">full schema hierarchy</a>. <a href="http://schema.org/">Schema.org</a>ās <a href="https://schema.org/docs/gs.html">Getting started with schema.org using Microdata</a> is a useful, concise guide, and that siteās <a href="https://validator.schema.org/">validator</a> will instantly show you the structured data that youāve added to your webpage with microdata.</p>
<h2 id="a-brief-note-on-laziness">A brief note on laziness</h2>
<p>Christmas is a time for relaxing. <a href="https://www.oreilly.com/library/view/programming-perl-4th/9781449321451/">Programming Perl</a> names laziness, impatience, and hubris as the three great virtues of a programmer, and this code offers us a good opportunity to practice those. Using microdata schemas from <a href="http://schema.org/">Schema.org</a> means that you probably wonāt need to spend time thinking up class names or IDs to use in your CSS; instead, you can just write <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors">attribute selectors</a> based on the microdata that youāve added, like so:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">[itemtype="https://schema.org/Book"] > [itemprop="name"]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"><span class="token property">font-weight</span> <span class="token punctuation">:</span> bold<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token property">font-size</span> <span class="token punctuation">:</span> large<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token property">margin-bottom</span> <span class="token punctuation">:</span> 1em<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h2 id="how-microdata-could-help-your-users">How microdata could help your users</h2>
<p>I got ahead of myself there; the <a href="https://www.w3.org/TR/html-design-principles/#:~:text=consider%20users%20over%20authors">HTML design principles</a> say to āconsider users over authorsā. So, here are some ways in which microdata can benefit your users:</p>
<h3 id="microdata-could-help-users-when-they-search-the-web">Microdata could help users when they search the web</h3>
<p>Mark Pilgrimās <a href="https://diveinto.html5doctor.com/extensibility.html">āāDistributed,ā āExtensibility,ā & Other Fancy Wordsā chapter</a> concluded with a hypothetical search result in which the search engine shows not only an excerpt of the text from the webpage, but also some of the microdata that he added. That does seem useful: it lends some credibility to the search result, and so should allow users to more easily find what theyāre looking for. This has persisted: Google Search Centralās <a href="https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data#:~:text=Adding%20structured%20data%20can%20enable%20search%20results%20that%20are%20more%20engaging%20to%20users%20and%20might%20%20encourage%20them%20to%20interact%20more%20with%20your%20website">Introduction to structured data markup in Google Search</a> says that āAdding structured data can enable search results that are more engaging to users and might encourage them to interact more with your website [ā¦]ā. To prove the point, Google Search Centralās <a href="https://developers.google.com/search/case-studies?hl=en">case studies</a> show how well-known organisations have used these techniques to improve search experiences for their users. However, the search engines may or may not show your webpageās microdata. It doesnāt seem to have happened for my āAuthors Apartā webpage yet, and Iām not suprised; Mark qualified his hypothetical example by saying, āAnd here (modulo the whims of Google, the phase of the moon, and so on and so forth) is what my review might look like in a search result listingā.</p>
<h3 id="microdata-could-help-users-when-they-use-artificial-intelligence-tools">Microdata could help users when they use Artificial Intelligence tools</h3>
<p>Similarly to Markās point about the whims of Google, Jono Aldersonās recent <a href="https://www.jonoalderson.com/conjecture/what-if-schema-org-is-just-labels/">What if Schema.org is just⦠Labels?</a> describes Googleās use of microdata as feeling ālimited, selective, inconsistent, and often frustratingly opaqueā. Jono goes on to highlight how the relationships that we define through microdata can help users who use AI tools. Of course, you might not consider this to be a net benefit: Molly Whiteās <a href="https://www.citationneeded.news/ai-isnt-useless/">AI isn't useless. But is it worth it?</a> weighs up the costs and benefits of these new tools.</p>
<h3 id="microdata-could-help-your-users-by-making-html-more-prevalent">Microdata could help your users by making HTML more prevalent</h3>
<p>This is my favourite benefit of using microdata, even if itās indirect. See, personally, I agree with the National Center on Disability and Access to Educationās <a href="https://www.ncdae.org/resources/factsheets/principles.php#:~:text=HTML%20is%20still%20the%20most%20accessible%20format%20for%20almost%20every%20type%20of%20content.">Principles of Accessible Design</a> when they said in 2007, āHTML is still the most accessible format for almost every type of contentā. So, it seems to me that the more we use HTML, the more accessible our content is for our users. My favourite recent example of this is Knut Hühneās <a href="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent">microdata-enhanced HTML Webcomponent for Leaflet</a>. (<a href="https://leafletjs.com/">Leaflet</a> is āan open-source JavaScript library for mobile-friendly interactive mapsā.) In a nice piece of progressive enhancement, Knut uses microdata to mark up geographical data from an API, so that basic address information is available to everyone on every browser, and graphical maps are available too.</p>
<p>To quote Mark Pilgrim one more time, āAngle brackets donāt impress me much, but I have to admit, thatās pretty cool.ā</p>
How I gained a new perspective on ARIA
2024-12-17T00:00:00Z
https://htmhell.dev/adventcalendar/2024/17/
by Marco Bretschneider<br><p>Can you remember the day you first learnt about ARIA? Maybe the first fact you learnt about <a href="https://www.w3.org/TR/using-aria/#NOTES">ARIA was the first rule</a> āDon't use ARIA, use native HTML insteadā - and so did I.</p>
<!-- MM: Du kannst davon ausgehen, dass Leute auf Social Media schreiben werden, dass das nicht die erste Regel ist. -->
<p>As someone who has been able to speak native HTML for many years, I always thought that ARIA is not relevant for a developer like me.<br />
How incredibly wrong I was...</p>
<h2 id="my-"wait-a-minute"-moment">My āWait a minuteā moment</h2>
<p>With the entry into force of the <a href="https://employment-social-affairs.ec.europa.eu/policies-and-activities/social-protection-social-inclusion/persons-disabilities/union-equality-strategy-rights-persons-disabilities-2021-2030/european-accessibility-act_en">European Accessibility Act</a>, accessibility has also become more important in our products, which are mainly used internally by banks.<br />
For this reason, we have subjected our products to an accessibility audit.<br />
One thing that really surprised me was that the auditor found fault with the following code.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>First navigation link<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>active<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Second navigation link<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- more navigation links --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.active</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* Highlight the selected navigation entry */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>When it comes to supporting people with visual impairments, it is our job as developers to convey as much information as possible that we also display to sighted people.<br />
People with visual impairments usually use a screen reader to consume the information on a page or application. A screen reader in turn obtains its information from the accessibility tree, which - together with the DOM tree - is generated in the browser.</p>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2024/17/a11y-tree-1-1.png" alt="The second linked selected in Firefox Dev Tools showing its accessible properties" loading="lazy" width="693" height="793" />
<figcaption>The code example above represented in the accessibility tree
</figcaption>
</figure>
<p>The information that the second entry in the navigation references the currently active page is not transported - unlike for sighted people.</p>
<h2 id="a-better-approach">A better approach</h2>
<p>By using ARIA, we can influence how the browser builds the accessibility tree. Specifically, ARIA provides the <code>aria-current</code> attribute for highlighting the currently active page.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>First navigation link<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">aria-current</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Second navigation link<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- more navigation links --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">[aria-current="page"]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* Highlight the selected navigation entry */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Now, the information that the second navigation entry references the currently displayed page is also transferred to the accessibility tree and can be used by screen readers.</p>
<p><img src="https://htmhell.dev/adventcalendar/2024/17/a11y-tree-2-1.png" alt="Firefox Dev Tools: Besides focusable, linked and more, the link now has an additional 'current' state in the accessibility tree" /></p>
<p>In addition, the <code>aria-current</code> attribute can also be used as a CSS hook for styling for sighted users. The <code>active</code> class initially used for this has therefore become obsolete.</p>
<h2 id="other-examples">Other examples</h2>
<p>There are other examples where we can enrich the accessibility tree using ARIA attributes, such as error messages for form fields.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>E-Mail<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>e-mail<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>email<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>error<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>Enter a valid e-mail address<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>The class <code>error</code> is used here to indicate fields that have been filled in incorrectly. For sighted users, the field is then styled accordingly using the class. Unfortunately, this information is not available in the accessibility tree.<br />
There is also an error message, but it is only visually linked to the input field and not programmatically (e.g., the label is linked to the input field).<br />
Unfortunately, we cannot enhance the accessibility tree with pure HTML. With ARIA, however, we can.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>E-Mail<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>e-mail<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>email<span class="token punctuation">"</span></span> <span class="token attr-name">aria-invalid</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<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>e-mail-error<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>e-mail-error<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Enter a valid e-mail address<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p><img src="https://htmhell.dev/adventcalendar/2024/17/a11y-tree-3.png" alt="Firefox Dev Tools: Besides focusable, editable and more, the input field now has an additional 'invalid' state in the accessibility tree" /></p>
<p>In the accessibility tree, we can see that the input field is marked as invalid and a relation to the error message is created.<br />
Once again, we can use the <code>aria-invalid</code> attribute as a CSS hook for styling. An <code>error</code> class is not required anymore.</p>
<h2 id="conclusion">Conclusion</h2>
<p>By using ARIA, we can influence the accessibility tree and enrich it with information that the browser cannot derive from native HTML. Screen reader vendors can use the information to create a better user experience for people who rely on assistive technologies.</p>
<p>The accessibility tree is built up of nodes that are typified by ARIA roles. The <a href="https://www.w3.org/TR/wai-aria-1.2/#roles_categorization">WAI ARIA specification</a> recognises around 90 different ARIA roles. They were defined because there is a sensible use for each of them. By using native HTML, however, only half of these roles are met. We can use the <code>role</code> attribute to influence the types of the nodes in the accessibility tree and achieve the entire spectrum - if it is appropriate for the use case. Ignoring ARIA and āonlyā using native HTML means that we are only using just under half of the richness that the accessibility tree provides.</p>
<p>I still don't know exactly when I heard about ARIA for the first time. But I can name the exact day when I had my wait a minute moment. Today, I know that the <a href="https://www.w3.org/TR/using-aria/#rule1">first rule of ARIA</a> is often abbreviated and a crucial detail of the rule is lost. Certain things cannot be expressed semantically in HTML at all and if ARIA offers a possibility here, we should make use of it.</p>
<p>Perhaps we should see ARIA as a counterpart to CSS. While we use CSS to enrich our HTML document for sighted users (progressive enhancement), we can use ARIA to enrich the accessibility tree for non-sighted users.</p>
<!-- MM: Hmm, ich weiĆ nicht. Das ist schon HTML und nicht ARIA. Du verleist mit dem Satz ARIA viel zu viel Bedeutung. Man kann keine Website ohne HTML bauen und eigentlich auch keine Website ohne CSS, aber man kann sehr wohl eine Website ohne ARIA bauen. Der vorherige Absatz ist eh schon ein gutes Schlusswort. -->
You donāt need the isOpen class
2024-12-16T00:00:00Z
https://htmhell.dev/adventcalendar/2024/16/
by Maureen Holland<br><p>Donāt get me wrong. You can keep it if you like it. But you donāt <em>need</em> it.</p>
<p>A class selector can allow us to visually show or hide content for disclosure widgets, like a custom select component or dropdown navigation menu. But a disclosure widget is made of two parts:</p>
<ol>
<li>A button that controls show/hide behaviour</li>
<li>Related content that is shown or hidden <em>depending on the button state</em></li>
</ol>
<p>Whether the <code>isOpen</code> class is applied or not, a screenreader would announce this button as <strong>"Click me!, button"</strong>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Click me!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>isOpen<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Content that displays depending on a conditionally applied class.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>One of the principles of web accessibility is to be perceivable. That means not relying exclusively on one sense, like sight, to provide information. In the above case, if I canāt see the content being shown or hidden, how do I know my button click did anything?</p>
<p>We <em>need</em> a corresponding semantic update to indicate what happened. This is what the <code>aria-expanded</code> attribute provides.</p>
<p>The following button would read: <strong>"Click me!, expanded, button"</strong>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-expanded</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Click me!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Content that displays depending on button's expanded state.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>This one would be <strong>"Click me!, collapsed, button"</strong>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-expanded</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>Click me!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Content that displays depending on button's expanded state.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code>aria-expanded</code> attribute also provides a ready-made CSS selector for collapsed and expanded states. So, we donāt need to add an <code>isOpen</code> class at all.</p>
<p>If youāve used the <code>:checked</code> pseudo-class before, youāll be familiar with the idea of styling scoped to a particular state. Itās a good idea. As a bonus, you get a mini accessibility test in your CSS! The styles only apply if the semantics are correct.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">button[aria-expanded="false"] + *</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* collapsed content styles here */</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">button[aria-expanded="true"] + *</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* expanded content styles here */</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>I first learned of this technique through <a href="https://inclusive-components.design/collapsible-sections/">Heydon Pickeringās Inclusive Components</a> and have been using it ever since. Itās simple, effective, and a great way to keep accessibility in mind at every stage of development.</p>
<p class="highlight">
<strong>Note:</strong> You will not need <code>aria-expanded</code> on the HTML <code>details</code> element. Stateful information is automatically provided through its <code>open</code> attribute. <a href="https://www.scottohara.me/blog/2022/09/12/details-summary.html">See Scott OāHara for more details on <code>details</code></a>.
</p>
The Gift You Do NOT Want: A Div in a Button's Clothing
2024-12-15T00:00:00Z
https://htmhell.dev/adventcalendar/2024/15/
by Corina Murg<br><p>With the right CSS makeup and a click event, almost anything can pretend to be a button. In accessibility work, we spot these fakes and fix them, but teaching others why and how to do it is just as important. Itās not just about correcting a single mistake; itās about introducing developers to accessibility concepts and approaches that they can comfortably return to and reuse across all their projects. More importantly, it encourages them to think beyond the sighted user with a mouse and consider the needs of those who rely on assistive technologies.</p>
<h2 id="really-how-bad-can-a-fake-button-be?!">Really, How Bad Can a Fake Button Be?!</h2>
<p>So, how do we answer this question? I used to demo the problems using a screen reader, but I've found that it can be overwhelming for developers new to accessibility. I prefer now a different approach, using simpler tools: the keyboard and the accessibility tree. They are easy to pick up and give developers a good feel for whatās really going on.</p>
<p>No, the message is not to avoid screen readers, but to start with tools that have a gentler learning curve. The tree itself gives developers plenty of clues about how each node may (or may not!) be recognized by assistive tech like screen readers or voice recognition software. And for whatever the tree canāt reveal, keyboard testing can fill in some of the gaps.</p>
<p>Of course, no tool is perfect, and the accessibility tree does have its limitations. Itās important that we remind developers of them as well. So, what exactly does this approach entail?</p>
<h2 id="build-a-genuine-button-with-a-lessbuttongreater">Build a Genuine Button with a <code><button></code></h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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>button one<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Change Color ONE</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">changeColor</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> element <span class="token operator">=</span> event<span class="token punctuation">.</span>currentTarget<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token keyword">const</span> currentColor <span class="token operator">=</span> <span class="token function">getComputedStyle</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">.</span>backgroundColor<span class="token punctuation">;</span></span><br /><span class="highlight-line"> element<span class="token punctuation">.</span>style<span class="token punctuation">.</span>backgroundColor <span class="token operator">=</span> currentColor <span class="token operator">===</span> <span class="token string">'rgb(0, 128, 0)'</span> <span class="token operator">?</span> <span class="token string">'rgb(58, 58, 242)'</span> <span class="token operator">:</span> <span class="token string">'rgb(0, 128, 0)'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">const</span> buttonOne <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.button.one'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">buttonOne<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> changeColor<span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>First, we create a genuine button using the <code><button></code> tag, and then examine how it appears in the accessibility tree. We focus on the attributes and properties exposed for this node, discussing what each one means in terms of interaction with assistive technologies. At every step, we also test with a keyboard.</p>
<p>Note: I regularly work on a Windows computer and am familiar with the accessibility tree in both Firefox and Chromium browsers. For this exercise, Iāll mostly use Firefox since it offers some clues about the shortcomings of fake buttons.</p>
<img src="https://htmhell.dev/adventcalendar/2024/15/images/Firefox-SemanticButton.jpg" alt="accessibility tree within Firefox's developer tools, highlighting the properties of a button element built with a button tag" width="500" aspect-ratio="658/693" loading="lazy" />
<p>So, what is the view from the accessibility tree revealing?</p>
<ul>
<li>
<p>The node has the role <code>button</code>, which comes with some privileges: it will be announced as a button by a screen reader, and since buttons support name from content, its text becomes its <code>accessible name</code>. So, a screen reader will announce it as <code>Change Color ONE, button</code>. Users relying on voice recognition software can activate it by voice command, such as saying <code>Click Change Color One</code>.</p>
</li>
<li>
<p>The <code>states</code> array shows that the button is focusable, so we know we can reach it via the <code>tab</code> key.</p>
</li>
</ul>
<p>But where does the keyboard come in? It helps us confirm the <code>focusable</code> property by allowing us to navigate to the button with the <code>tab</code> key. Plus, it confirms that we can activate the button using the <code>Enter</code> key or the <code>Space</code> bar, another built-in perk of genuine buttons that is not immediately obvious from the accessibility tree.</p>
<p>Next step: build a fake button.</p>
<h2 id="build-a-fake-button-with-a-lessdivgreater">Build a Fake Button with a <code><div></code></h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button two<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Change Color TWO</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token keyword">const</span> buttonTwo <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.button.two'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">buttonTwo<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> changeColor<span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>We simply attach a click event to a <code>div</code>, and with nearly the same CSS as weād use for a genuine button, we create something a sighted user will recognize as a button.</p>
<p>Hereās the view from Firefoxās accessibility tree:</p>
<img src="https://htmhell.dev/adventcalendar/2024/15/images/Firefox-FakeButtonNotFixed.jpg" alt="accessibility tree within Firefox's developer tools, highlighting the properties of a div element used as a button" width="500" aspect-ratio="660/729" loading="lazy" />
<p>What should we be looking or testing for? The same attributes and behaviors we found for the genuine button:</p>
<ul>
<li>role,</li>
<li>name,</li>
<li>focusable property, and</li>
<li>keydown and keyup events.</li>
</ul>
<p>The nodeās role defaults to <code>generic</code> since the building block is a <code>div</code>. It also remains unnamed because generic nodes donāt have naming privileges, despite containing text. Keyboard users will not reach it, let alone activate it, and we donāt even have to test with a keyboard to prove it. We simply notice that the <code>states</code> array does not list the <code>focusable</code> property.</p>
<p>Once we make it focusable by adding a <code>tabindex="0"</code> we notice something else: we can't activate it with the <code>Enter</code> key or the <code>Space</code> bar. Is this lack of functionality reflected in the accessibility tree as well? Sort of. The genuine button lists a <code>Press</code> event under the <code>actions</code> property, while the fake button only shows <code>Click</code>. But more on that later.</p>
<p>Firefox is also explicitly warning us about the lack of focus and "interactive semantics", and it's connecting us to MDN for more information. So, what are we still missing? We need:</p>
<ul>
<li>a <code>keydown</code> event for activation via <code>Enter</code>,</li>
<li>a <code>keyup</code> event for activation via <code>Space</code>, and</li>
<li>a <code>button</code> role to have it announced properly by screen readers, and also to assign it a name based on its content.</li>
</ul>
<p>Here's what the HTML would look like with all the additions:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button two<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>button<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 punctuation">></span></span></span><br /><span class="highlight-line"> Change color TWO</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>while the JavaScript would have to include the following as well:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line">buttonTwo<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></span><br /><span class="highlight-line"> <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">'Enter'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> event<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">changeColor</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">buttonTwo<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'keyup'</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></span><br /><span class="highlight-line"> <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">' '</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> event<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">changeColor</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>For developers it might seem tempting to add these missing features one by one. The advice, of course, is that the simpler and more robust solution is to replace the <code><div></code> with a <code><button></code>. However, for the sake of exploring what the accessibility tree reveals ā or fails to! ā and the importantce of manual testing, weāll go ahead and add all the necessary code to make this <code>div</code> fully functional.</p>
<h2 id="the-accessibility-tree-can-only-do-so-much">The Accessibility Tree Can Only Do So Much</h2>
<p>Is the retrofitted <code>div</code> recognized as a button now? Yes. It has the role <code>button</code>, it has a name, and it's focusable.</p>
<!-- updated -->
<img src="https://htmhell.dev/adventcalendar/2024/15/images/Firefox-FakeButtonFixed.jpg" alt="accessibility tree within Firefox's developer tools, highlighting the properties of button built with a div tag" width="500" aspect-ratio="658/693" loading="lazy" />
<p>Is the retrofitted button's tree view different in any way from the one of the genuine button? Yes again.</p>
<p>First, the <code>DOMNode</code> property reminds us that we started with a div. Next, something curious happens within the <code>actions</code> array.</p>
<p>Since weāve added the <code>keydown</code> and <code>keyup</code> events we would not expect any differences between the <code>actions</code> arrays of the two buttons, right? Wrong. The updated view still shows <code>Click</code> instead of <code>Press</code>. It seems that manually adding the keyboard events does not change the action keyword. But is this a Firefox oversight, or just a subtle dig at fake buttons? Just kidding!</p>
<p>It's worth noting that the Chromium browsers also take the approach of differentiating between the two buttons. They do not display this information as part of the accessibility tree, but separately, under the <code>Event Listeners</code> tab in DevTools. For the genuine button, their keyword of choice is <code>click</code> and not <code>Press</code>, but then, just like Firefox, they treat the div button differently. When keyboard events are explicitly added, Chromiumās event listeners include <code>click</code> along with <code>keydown</code> and <code>keyup</code>.</p>
<img src="https://htmhell.dev/adventcalendar/2024/15/images/Chrome-FakeButtonFixedEvents.jpg" alt="accessibility tree within Chrome's developer tools, highlighting a button built with a div tag. Click, keydown and keyup are listed under event listeners" width="500" aspect-ratio="670/258" loading="lazy" />
<p>The Chromium behavior makes sense: when we attach a click handler to a native button, it automatically works for all input methods (mouse, touch, keyboard). That's why it only shows one <code>click</code> listener linking to one particular function. For the div button, since we manually added separate keyboard handlers, DevTools shows links to all three function implementations - one for click and two for keyboard events.</p>
<p>But enough nerding over keywords and browser inconsistencies, as interesting as this detective work might be. The point to make is that on keyboard interactions the browsers send mixed signals, and that testing, in this case keyboard testing, is irreplaceable. (Is this my subtle way of turning your attention away from the fact that I don't have (yet!) an explanation about these inconsistencies? Totally! I've spent time poking around the Firefox and Chromium codebases, but so far the answer remains a mystery.)</p>
<p>Let's end with a keyboard interaction anomaly that only keyboard testing could reveal: while theoretically both buttons are now fitted with proper keyboard support, there's an important difference in how they handle the <code>keyup</code> event. The benefit of a <code>keyup</code> event is that the user can cancel the action on a button. They can press <code>Space</code>, change their mind, move focus away with <code>Tab</code>, and release <code>Space</code> without triggering the button. When we build our own button from a div, we need extra code to track whether the <code>Space</code> key was initially pressed while focused on that button. Without this check, it would <a href="https://codepen.io/Cor-Ina/pen/OPLJYry">incorrectly activate when receiving focus</a> while <code>Space</code> is being released, even if <code>Space</code> was originally pressed on a different element. And while we add more code to our creation, the browsers will, of course, take good care of the genuine button and gracefully ignore Space keyup events that don't belong to it.</p>
<!-- CM: optional paragraph. Not sure if this anomaly is real or just a symptom of the code I used to implement the keyup event. A CodePen to test: https://codepen.io/Cor-Ina/pen/OPLJYry -->
<!-- MM: Interesting. Will just publish this and see if anyone comments. :) -->
<h3 id="takeaways-for-developers">Takeaways for Developers</h3>
<ol>
<li>
<p>Semantic HTML is powerful. Always consider it first.</p>
</li>
<li>
<p>No tool is perfect, and the accessibility tree and the DevTools in general are no exception. Testing is key. In our particular case, testing with the keyboard is enough, but in other scenarios testing with a screen reader will be necessary. In general, accessibility requires a holistic approach and that we select tools and tests based on the specific functionality we are assessing.</p>
</li>
</ol>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Does this approach work? At the minimum, it introduces developers to the accessibility tree and reinforces the usefulness of a familiar tool, the ubiquitous keyboard. If it ends with "Now I know I shouldn't just add a click event to a div" then yes, it does work.</p>
<p>From experience, it can sometimes make developers nervous about their ability to choose the right tools or worry about overlooking crucial details. This is still a win. It shows that they care. If you've had any accessibility mishaps in your past, this might be actually a good moment to reveal them, and prove that progress is possible. If you've had none, feel free to use one of mine: before I started using screen readers, I used to add a <code>tabindex</code> to headings.</p>
<p>What should we encourage developers to focus on, no matter their initial reaction? Know their HTML, consider the needs of different types of users, lean into testing. And ask questions.</p>
Page by Page: How Pagination Makes the Web Accessible
2024-12-14T00:00:00Z
https://htmhell.dev/adventcalendar/2024/14/
by Kristin Rohleder<br><p>Imagine youāre reading a book that seems perfect for cozy winter evenings. But as soon as you turn the page, you suddenly find yourself somewhere else, rather than on the next page of the story. Now, you have to painstakingly search through the book to find where the story continues ā as if someone had bound the pages in the wrong order.<br />
Would you keep reading? Or recommend the book to others?</p>
<p>For about <a href="https://www.who.int/news-room/fact-sheets/detail/disability-and-health#:~:text=Key%20facts,1%20in%206%20of%20us.">16% of the worldās population</a> (roughly the population of China), the web often feels like a maze. People who rely on assistive technologies experience the web like a poorly bound book, one they must tediously navigate page by page rather than simply moving to the next relevant section.</p>
<p>Another example: imagine you're searching on Google and find the answer on page 2. However, to get there, you first need to enter the <a href="https://en.wikipedia.org/wiki/Konami_Code">Konami Code</a> to unlock the link. Such obscured navigation elements are, unfortunately, a daily reality for many web users.<br />
On some websites, pagination links are hidden behind inaccessible images without labels, or they use faulty ARIA attributes that make navigation difficult for screenreader users.</p>
<p>While an accessible pagination doesn't make the entire web accessible, it does ensure that everyone, whether reading a book or browsing the web, can seamlessly turn from page to page and easily find the next part of the story.</p>
<h2 id="pagination-i">Pagination I</h2>
<p>Letās start with a simple example ā a pagination thatās perfect for smaller blog pages or product overviews with a limited number of items.</p>
<p><img src="https://htmhell.dev/adventcalendar/2024/14/paginationI.jpg" alt="A pagination with a previous button, 5 numbered buttons, and a next button. Illustration." /></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>More Christmas cookie recipes<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>link<span class="token punctuation">"</span></span> <span class="token attr-name">aria-disabled</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Previous<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">aria-current</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/2/<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"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/3/<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"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/4/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>4<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/5/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>5<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/2/<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>Next, Page 2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Next<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<p><strong>Clear Page Structure</strong></p>
<ul>
<li>The <code><nav></code> element with a descriptive <code>aria-label</code> provides orientation for screenreader users and makes the <a href="https://www.htmhell.dev/tips/landmarks/">page structure</a> immediately recognizable.</li>
<li>If <code><nav></code> cannot be used, the root element of the pagination can be assigned <code>role="navigation"</code> to ensure assistive technologies interpret the navigation correctly.</li>
<li>Distinct <code>aria-label</code>s (e.g. "Pagination of product listings" or "More cookie recipes") help differentiate between navigation areas and provide additional context for the links.</li>
</ul>
<p><strong>Using</strong> <code><ul></code> <strong>or</strong> <code><ol></code> <strong>for Flexibility</strong></p>
<ul>
<li>A list can improve the user experience for screenreader users. However, screenreaders interpret <code><ul></code> and <code><ol></code> differently, which can lead to confusion depending on the context:
<ul>
<li>With a <code><ul></code> element, a link to the second page might be read as "Bullet, Link, 2," signaling that the pages are considered equal and no strict order is implied.</li>
<li>With an <code><ol></code> element, the same link might be read as "3, Link, 2," because the position within the list (in this case, the third <code><li></code> element) is also announced. This can be confusing when the announced number doesnāt match the actual page number in the link, such as in paginations that begin with additional links like āPrevious.ā</li>
</ul>
</li>
<li>For small, sequential navigation, such as a multi-page news article, an <code><ol></code> is a good choice. Here, the announced order matches the page structure, helping assistive technologies convey the context. For non-linear content, where order doesnāt matterālike pages with multiple Christmas cookie recipesāa <code><ul></code> can be the better option.</li>
</ul>
<p><strong>Proper Deactivation</strong></p>
<ul>
<li>A <a href="https://www.scottohara.me/blog/2021/05/28/disabled-links.html">disabled link</a>, such as āPreviousā to the first page, should not have an <code>href</code> attribute. Since the <code>disabled</code> attribute only applies to form controls like <code><button></code>, <code>aria-disabled="true"</code> is used instead.</li>
<li>Additionally, the <code>role="link"</code> attribute should be added to explicitly define the elementās role as a link. An <code><a></code> element without an <code>href</code> attribute does not have a specific default role and is treated inconsistently by assistive technologies. The combination of <code>aria-disabled="true"</code> and <code>role="link"</code> ensures that the <a href="https://w3c.github.io/html-aria/#example-communicate-a-disabled-link-with-aria">link is correctly perceived as disabled</a> without being mistakenly interpreted as āclickable.ā</li>
<li>Alternatively, a <code><span></code> element can replace the link, e.g. <code><span>Previous</span></code>. This method is considered a best practice because it simplifies the code and minimizes the use of ARIA attributes. This aligns with the principle that native HTML elements and semantic structures should be preferred to improve code maintainability and clarity. Both approaches are valid as long as they are implemented correctly.</li>
</ul>
<p><strong>Intuitive Navigation</strong></p>
<ul>
<li>The <code>aria-current="page"</code> attribute indicates which page is currently open. Screenreaders interpret this as āCurrent page, Link, 1ā or similar. This helps users quickly orient themselves and easily identify which page they are on.</li>
</ul>
<p><strong>Clear Link Descriptions</strong></p>
<ul>
<li>A visible link text such as ā2ā for the second page is sufficient for assistive technologies and their users to interpret the link correctly. However, if an additional <code>aria-label="Page 2"</code> were added, the link would be announced by screenreaders as āPage 2ā.</li>
<li>Sighted users relying on voice control might attempt to activate the link by saying ā2ā because they see that number. The voice control software, would not find the link because the link name (accessible name) does not match the visible text. To avoid this, the visible text should always be identical to the accessible name.</li>
<li>Furthermore, the <code>aria-label</code> of the surrounding <code><nav></code> element (e.g. <code>aria-label="More pages of product listings"</code>) already provides the necessary context that the links are for pagination.</li>
<li>An exception can be made if the visible link text is integrated at the beginning of the <code>aria-label</code>, such as <code>aria-label="next, page 2"</code>. Here, ānextā comes first to describe the function of the link, while āpage 2ā provides additional context. This structure is helpful as it emphasizes where the link leads.</li>
</ul>
<h2 id="pagination-ii">Pagination II</h2>
<p>Letās look at a pagination thatās particularly suited for blog pages with many entries or product listing pages with extensive products. This advanced pagination allows users to skip multiple pages in the display while maintaining a clear structure.</p>
<p>This pagination builds on the principles of the simple pagination. The same recommendations for using <code><nav></code>, <code><ul></code>, <code>aria-label</code>, <code>aria-current</code>, and <code>aria-disabled</code> apply here as well.</p>
<p><img src="https://htmhell.dev/adventcalendar/2024/14/paginationII.jpg" alt="A pagination with a button with two chevrons pointing left, a button with one chevrons pointing left, followed by an ellipsis, 3 numbered buttons (7,8, and 9), another ellipsis, and finally a button with two chevrons pointing right, a button with one chevrons pointing right. Illustration." /></p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* styles for elements that are only relevant for screenreader users */</span></span><br /><span class="highlight-line"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> -1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border-width</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>More Christmas cookie recipes<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/1/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First Page<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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 attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>16<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>16<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 24 24<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M14 18l-6-6 6-6L12.59 4 6.59 10 12.59 16 14 18z<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M20 18l-6-6 6-6L18.59 4 12.59 10 18.59 16 20 18z<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/7/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>previous-page-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>Previous Page, Page 7<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>skipping pages 1 to 6<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/7/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>7<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">aria-current</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/9/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>9<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>skipping pages 10 to 12<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/9/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>next-page-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>Next Page, Page 9<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/12/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Last Page, Page 12<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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 attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>16<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>16<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 24 24<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M10 6l6 6-6 6 1.41 1.41L18.41 12 11.41 4.59 10 6z<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M4 6l6 6-6 6 1.41 1.41L12.41 12 5.41 4.59 4 6z<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<p><strong>Using SVGs</strong></p>
<ul>
<li>Icons should always have descriptive text to make their function clear for screenreader users. Instead of describing the SVG itself (e.g. āDouble arrow pointing leftā), the purpose should be communicated (e.g. āFirst pageā).</li>
<li>Using a <code><span></code> element with the <a href="https://webaim.org/techniques/css/invisiblecontent/">screenreader-only class .sr-only</a> can be a practical approach. This class hides the text visually for sighted users but makes it accessible to screenreaders. In certain cases, it may have an advantage over using an <code>aria-label</code> on the <code><a></code> element because screenreaders read the text with the correct language and pronunciation preferences.
<ul>
<li>Tested Behavior: When using NVDA (Version 2024.4) with Chrome (Version 130) and German as the language preference, English text in an <code>aria-label</code> was read with a German voice and pronunciation. However, when the text was provided via the <code>.sr-only</code> class, it was read correctly with an English voice. This behavior also has been documented in <a href="https://github.com/nvaccess/nvda/issues/16285">NVDA GitHub Issue #16285</a>.</li>
<li>With VoiceOver on macOS, no difference between the two methods was observed.</li>
<li>Note: This has not been tested with JAWS or other screenreaders and may vary depending on the browser and screenreader combination.</li>
</ul>
</li>
<li>The <code>role="presentation"</code> attribute should be added to SVG elements to prevent some screenreaders from announcing them as standalone elements (e.g. āLink, Image, First pageā). This ensures they are interpreted as purely decorative and not announced (e.g. āLink, first pageā).</li>
</ul>
<p><strong>Using Images</strong></p>
<ul>
<li>When an <code><img></code> element is the only child of a link, the text of the <code>alt</code> attribute is automatically read by screenreaders as the link text. Therefore, the <code>alt</code> attribute should include a descriptive text that clearly conveys the function of the link (e.g. <code>alt="Next Page, Page 9"</code>). This method is a best practice as it is natively supported by screenreaders and requires no additional effort.</li>
<li>If it is not possible to modify the <code>alt</code> attribute of the image due to technical constraints, an <code>aria-label</code> on the <code><a></code> element can be used instead to describe the function of the link. However, this solution should only be applied in exceptional cases.</li>
</ul>
<p><strong>Skipping Multiple Pages</strong></p>
<ul>
<li>In reading mode, screenreaders typically do not announce ellipses (ā...ā) used to indicate skipped pages. Instead, the list item is often read as an empty bullet, which can confuse users as it does not explain why no additional information is provided at that point.</li>
<li>To ensure that skipping pages is clearly communicated, an explanatory text such as āskipping pages 10 to 12ā should be added using the <a href="https://webaim.org/techniques/css/invisiblecontent/">screenreader-only class .sr-only</a>. This text will be read by screenreaders but remain invisible to sighted users. An <code>aria-label</code> is not a suitable alternative here, as it cannot be applied to generic elements like <code><span></code> or <code><div></code>.</li>
<li>This approach is particularly helpful when page skipping occurs in the middle of pagination, as it provides screenreader users with context and prevents confusion about why navigation jumps from one page (e.g. page 2) to another (e.g. page 7). The additional context improves orientation and reduces potential misunderstandings.</li>
</ul>
<h2 id="the-final-chapter">The final chapter</h2>
<p>Accessible pagination is more than just a design detailāit provides orientation, simplifies navigation, and greatly improves the user experience, especially for those relying on assistive technologies.</p>
<p>The semantic HTML elements and ARIA attributes presented here create an accessible structure that remains easy to use across all devices, from desktop to smartphone.</p>
<p>Accessible pagination ensures that all users can navigate the web without "getting lost as if reading a book with pages bound out of order." This makes the web not only more inclusive but also more accessible for everyone looking to find content efficiently and directly.</p>
<h2 id="testing-and-validation">Testing and validation</h2>
<p>To ensure the proposed solutions deliver on their promise, I conducted tests in different environments:</p>
<ul>
<li>HTML Validation: The code was tested using the <a href="https://validator.w3.org/nu/">W3C HTML Validator</a> with no errors or warnings found.</li>
<li>Tested on <a href="https://assistivlabs.com/">AssistivLabs</a>, a platform for remote testing of assistive technologies:
<ul>
<li>Screenreader: NVDA (Version 2024.4)</li>
<li>Browser: Chrome (Version 130)</li>
</ul>
</li>
<li>Tested on a real device:
<ul>
<li>Operating System: macOS 15.1.1</li>
<li>Screenreader: VoiceOver</li>
<li>Browsers: Chrome (Version 131) and Firefox (Version 133)</li>
</ul>
</li>
</ul>
<p>However, additional testing with other screenreader and browser combinations is recommended to ensure compatibility across a wider range of devices and tools.</p>
Improving User Experience for Multilingual Web Browsing
2024-12-13T00:00:00Z
https://htmhell.dev/adventcalendar/2024/13/
by Anastasiia Batarei<br><p>Today, Iād like to talk about the experience of browsing websites where content is fully or partially in a language different from the userās native one. This situation is common for users navigating government portals or local service providers in a country where they don't speak the language well, and for international marketplaces, where essential information might appear in unfamiliar language. Or just a user has a different language preference set in their browser because it's convenient for them. In these cases, users often rely on the browserās auto-translate feature, which many modern browsers offer by default. When you open a page in a different language from your preferred setting, the browser will prompt you to translate it automatically.</p>
<img src="https://htmhell.dev/adventcalendar/2024/13/google-translate-popup.png" width="800" height="161" loading="lazy" alt="Language selection options in Google Translate: Dutch and English." />
<p>Browser auto-translation relies on the lang attribute set on the HTML element:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<p>However, automatic translations can sometimes be inaccurate or misleading, especially when it comes to proper and branded names, or technical terms that should remain untranslated.</p>
<p>For example, letās see what happens when a userās name, like <em>Joke</em> (a Dutch feminine name pronounced [ĖjoĖkÉ]), is displayed on an English page that is then auto-translated into Dutch:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Original content in English --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Name: Joke<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- Translated content in Dutch --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Naam: Grap<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>The Dutch word <em>āGrapā</em> means <em>ājokeā</em> ā a thing said to cause amusement ā turning the name into something unintended and causing confusion.</p>
<p>To prevent such misinterpretations, we can use the <code><a href="https://html.spec.whatwg.org/multipage/dom.html#the-translate-attribute">translate</a></code> attribute, which indicates whether an element content should or should not be translated. The attribute can appear on any element, and it takes just two values: <code>yes</code> or <code>no</code>. In our case, we need to use the value <code>no</code> to disable automatic translation for specific pieces of content:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Original content in English --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Name: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">translate</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Joke<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- Translated content in Dutch --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Naam: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">translate</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Joke<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="translation-in-context">Translation in Context</h2>
<p>Translation engines have improved significantly over time and can often understand whether certain content, like email addresses or well-known names (e.g., <em>Pain au chocolat</em>), should be translated or not. However, it's still crucial to manually test your content to avoid unexpected issues. For instance, email addresses that appear as part of a search results page might be wrapped in additional HTML tags, which can cause translation errors. Letās compare these two examples:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Original content in English --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>E-mail: joke@example.com<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>E-mail: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mark</span><span class="token punctuation">></span></span>joke<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mark</span><span class="token punctuation">></span></span>@example.com<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- Translated content in Dutch --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>E-mailadres: joke@example.com<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>E-mailadres: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mark</span><span class="token punctuation">></span></span>grap<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mark</span><span class="token punctuation">></span></span>@voorbeeld.com<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>The issue can be easily avoided by using the <code>translate="no"</code> attribute to ensure that the email address stays untranslated:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Original content in English --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> E-mail: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">translate</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mark</span><span class="token punctuation">></span></span>joke<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mark</span><span class="token punctuation">></span></span>@example.com<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- Translated content in Dutch --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> E-mailadres: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">translate</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mark</span><span class="token punctuation">></span></span>joke<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mark</span><span class="token punctuation">></span></span>@example.com<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>With this adjustment, the display remains accurate across languages, preventing incorrect translations and enhancing user trust. Misleading translations can create uncertainty and may cause users to double-check information unnecessarily, impacting accessibility, usability, and overall experience. Ideally, users should navigate with confidence, and as developers, we can do this small trick to make their experience smoother.</p>
<h2 id="non-standardized-approach">Non-standardized approach</h2>
<p>Before the <code>translate</code> attribute was part of the HTML specification, <a href="https://www.w3.org/International/questions/qa-translate-flag#stickyness">translation engines offered similar tools</a>. For example, Google and Microsoftās translation engines support the class <code>notranslate</code>, which worked similarly:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Name: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>notranslate<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Joke<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>However, using the <code>translate="no"</code> attribute, which is now part of the HTML specification, is a more robust and reliable approach, ensuring consistency across current and future translation APIs and localization standards.</p>
<p class="highlight"><strong>Note:</strong> All examples were tested in Chrome Version 129.0.6668.103 (Official Build, x86_64).</p>
HTML and CSS I didn't even know about before I started creating content in Japanese
2024-12-12T00:00:00Z
https://htmhell.dev/adventcalendar/2024/12/
by Julia Undeutsch<br><p>Since I started to create content in Japanese, I also wanted to learn about traditional setups, like having Japanese text flow from top to bottom, right to left, like youād see in newspapers or novels. That's when I discovered CSS properties like <code>writing-mode: vertical-rl</code> and HTML tags like <code><ruby></code>, which add <a href="https://en.wikipedia.org/wiki/Furigana">furigana (phonetic guides)</a> over <a href="https://en.wikipedia.org/wiki/Kanji">kanji characters</a>.</p>
<p>Honestly, Iād never used these properties before and almost forgot they even existed! But now that Iāve dived into them, Iāll break down how you can implement them step-by-step to get that traditional Japanese look.</p>
<style>
.step1 {
font-size: 2rem; /* Adjust font size as needed */
letter-spacing: 0.2em; /* Ensures legibility */
line-height: 1.5; /* Adjust line height for spacing */
text-orientation: mixed; /* Allow for mixed orientation (e.g., hiragana and kanji) */
writing-mode: vertical-rl; /* Set writing mode to vertical, right to left */
}
.vertical-text {
font-size: 2rem; /* Adjust font size as needed */
letter-spacing: 0.2em; /* Ensures legibility */
line-height: 1.5; /* Adjust line height for spacing */
text-orientation: mixed; /* Allow for mixed orientation (e.g., hiragana and kanji) */
writing-mode: vertical-rl; /* Set writing mode to vertical, right to left */
}
</style>
<p>That's the text we'll work with. It means "Example of vertical text. Japanese culture is very rich.":</p>
<div class="step0">
<h1>
ēø¦ęø(ćć¦ć)ćć®ććć¹ćć®ä¾(ćć)
</h1>
<p>
ę„ę¬(ć«ć»ć)ć®ęå(ć¶ćć)ćÆćØć¦ćč±(ćć)ćć§ćć
</p>
</div>
<h2 id="step-1-setting-up-vertical-text-with-css">Step 1: Setting Up Vertical Text with CSS</h2>
<p>First up, weāll make the text flow vertically from right to left, top to bottom. The CSS property <code>writing-mode: vertical-rl</code> is perfect for this. Itās how you make Japanese text look like itās traditionally printed.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.vertical-text</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span> <span class="token comment">/* Adjust font size as needed */</span></span><br /><span class="highlight-line"> <span class="token property">letter-spacing</span><span class="token punctuation">:</span> 0.2em<span class="token punctuation">;</span> <span class="token comment">/* Ensures legibility */</span></span><br /><span class="highlight-line"> <span class="token property">line-height</span><span class="token punctuation">:</span> 1.5<span class="token punctuation">;</span> <span class="token comment">/* Adjust line height for spacing */</span></span><br /><span class="highlight-line"> <span class="token property">text-orientation</span><span class="token punctuation">:</span> mixed<span class="token punctuation">;</span> <span class="token comment">/* Allow for mixed orientation (e.g., hiragana and kanji) */</span></span><br /><span class="highlight-line"> <span class="token property">writing-mode</span><span class="token punctuation">:</span> vertical-rl<span class="token punctuation">;</span> <span class="token comment">/* Set writing mode to vertical, right to left */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<!-- MM: Added another demo here -->
<div class="step1">
<h1>
ēø¦ęø(ćć¦ć)ćć®ććć¹ćć®ä¾(ćć)
</h1>
<p>
ę„ę¬(ć«ć»ć)ć®ęå(ć¶ćć)ćÆćØć¦ćč±(ćć)ćć§ćć
</p>
</div>
<p><code>writing-mode: vertical-rl</code>: This property turns the text to vertical, flowing from the top to bottom, starting on the right side of the page.</p>
<p><code>text-orientation: mixed</code>: This keeps kanji and other characters readable. Japanese text often mixes different character types, so mixed is generally the best setting here.</p>
<p><code>font-size</code>, <code>letter-spacing</code> and <code>line-height</code>: Adjust these to make the text readable and nicely spaced.</p>
<h2 id="step-2-adding-margins-for-better-spacing">Step 2: Adding Margins for Better Spacing</h2>
<!-- MM: Shouldn't this be step 3? Add ruby first and then tweak the styling. -->
<p>When working with vertical text, you can add spacing between blocks with <code>margin-block</code>. This CSS property acts like <code>margin-top</code> and <code>margin-bottom</code> for horizontal text, and <code>margin-left</code> and <code>margin-right</code> for vertical text.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">ruby rt</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 0.7rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin-block</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span> <span class="token comment">/* Adds some space between text and ruby text */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The property comes in very handy when working with multiple languages on your website, because it adjusts spacing based on the flow direction of the text.</p>
<h2 id="step-3-adding-furigana-with-lessrubygreater">Step 3: Adding Furigana with <code><ruby></code></h2>
<p>In Japanese, furigana (small <a href="https://en.wikipedia.org/wiki/Hiragana">hiragana</a> or <a href="https://en.wikipedia.org/wiki/Katakana">katakana</a> text) is often placed above kanji characters to show pronunciation. We can add furigana with the <code><ruby></code> and <code><rt></code> tags, which are specifically meant for this purpose.</p>
<p>Hereās how to add furigana:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>vertical-text<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ruby</span><span class="token punctuation">></span></span>ę„ę¬<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rt</span><span class="token punctuation">></span></span>ć«ć»ć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ruby</span><span class="token punctuation">></span></span></span><br /> ć®<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ruby</span><span class="token punctuation">></span></span>ęå<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rt</span><span class="token punctuation">></span></span>ć¶ćć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ruby</span><span class="token punctuation">></span></span> ćÆćØć¦ć<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ruby</span><br /><span class="highlight-line"> <span class="token punctuation">></span></span>č±<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rt</span><span class="token punctuation">></span></span>ćć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ruby</span></span><br /> <span class="token punctuation">></span></span><br /><span class="highlight-line"> ćć§ćć</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<!-- MM: Added another demo here -->
<div class="vertical-text">
<h1>
<ruby>ēø¦ęø<rp>(</rp><rt>ćć¦ć</rt><rp>)</rp></ruby>ćć®ććć¹ćć®<ruby>ä¾<rp>(</rp><rt>ćć</rt><rp>)</rp></ruby>
</h1>
<p>
<ruby>ę„ę¬<rp>(</rp><rt>ć«ć»ć</rt><rp>)</rp></ruby>ć®<ruby>ęå<rp>(</rp><rt>ć¶ćć</rt><rp>)</rp></ruby>ćÆćØć¦ć<ruby>č±<rp>(</rp><rt>ćć</rt><rp>)</rp></ruby>ćć§ćć
</p>
</div>
<p><code><ruby></code>: Wraps around the kanji characters.</p>
<p><code><rt></code>: Contains the furigana, displayed above the kanji.</p>
<p><code><rp></code>: Provides fall-back parentheses for browsers that do not support display of ruby annotations.</p>
<h2 id="step-4-putting-it-all-together">Step 4: Putting It All Together</h2>
<p>Now, letās combine everything in a full HTML example. This includes vertical text styling, spacing, and furigana for a traditional Japanese feel:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ja<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Vertical Japanese Text Example<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /><span class="highlight-line"> <span class="token selector">*</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">body</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">justify-content</span><span class="token punctuation">:</span> flex-end<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 25px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">.vertical-text</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">"Noto Sans JP"</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 1.5rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">letter-spacing</span><span class="token punctuation">:</span> 0.2em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">line-height</span><span class="token punctuation">:</span> 1.5<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">text-orientation</span><span class="token punctuation">:</span> mixed<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">writing-mode</span><span class="token punctuation">:</span> vertical-rl<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">h1</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 2.5rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin-block</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">ruby rt</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 0.7rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin-block</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>vertical-text<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ruby</span><span class="token punctuation">></span></span>ēø¦ęø<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rt</span><span class="token punctuation">></span></span>ćć¦ć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ruby</span><span class="token punctuation">></span></span>ćć®ććć¹ćć®<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ruby</span><br /><span class="highlight-line"> <span class="token punctuation">></span></span>ä¾<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rt</span><span class="token punctuation">></span></span>ćć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ruby</span></span><br /> <span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ruby</span><span class="token punctuation">></span></span>ę„ę¬<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rt</span><span class="token punctuation">></span></span>ć«ć»ć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ruby</span><span class="token punctuation">></span></span>ć®<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ruby</span><br /><span class="highlight-line"> <span class="token punctuation">></span></span>ęå<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rt</span><span class="token punctuation">></span></span>ć¶ćć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ruby</span></span><br /> <span class="token punctuation">></span></span>ćÆćØć¦ć<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ruby</span><span class="token punctuation">></span></span>č±<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rt</span><span class="token punctuation">></span></span>ćć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rt</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rp</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rp</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ruby</span><span class="token punctuation">></span></span>ćć§ćć<br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<p>And there you have it! With just a few CSS properties and HTML tags, you can transform your text to reflect a more traditional Japanese reading style.</p>
<p>å®éØć愽ććļ¼</p>
<p><em>Enjoy experimenting!</em></p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="poMawje" data-pen-title="Japanese Example" data-user="YuriDevAT" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/YuriDevAT/pen/poMawje">
Japanese Example</a> by Julia Undeutsch (<a href="https://codepen.io/YuriDevAT">@YuriDevAT</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/text-orientation">text-orientation on MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode">writing-mode on MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/margin-block">margin-block on MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby">ruby on MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp">rp on MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt">rt on MDN</a></li>
</ul>
Makeshift hot reload
2024-12-11T00:00:00Z
https://htmhell.dev/adventcalendar/2024/11/
by Evan Hahn<br><p><em>In short: put <code><meta http-equiv="refresh" content="1"></code> in your <code><head></code> element to refresh your page every second. This is a makeshift "hot reload" for development. It's not perfect, but it can be a quick solution!</em></p>
<p>Hot reloading automatically reloads parts of your page while you're working. Change some HTML and see your work instantly! Tweak some CSS and the results are right there! Gone are the days of repeatedly switching to your browser to refresh, because hot reload takes care of it.</p>
<p>It's called "hot" reloading because it's <em>hot</em>. It's powerful. Everyone loves it.</p>
<p>But here's the problem: hot reload requires fancy build tooling. It's built into tools like Vite, Parcel, and various editor plugins. But what if you aren't using one of these? What if you're writing HTML the way God intended: by editing some damn files on disk? In other words, without a build tool?</p>
<p>Can you still get that hot, hot reloading?</p>
<h2 id="home-grown-hot-reload">Home-grown hot reload</h2>
<p>You can create a makeshift hot reload by putting this somewhere in your <code><head></code> tag:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Reload the whole page every second. --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>refresh<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>Adding this will make the whole page reload every second.</p>
<p>Now, when you edit some HTML, you'll only have to wait a second before you see your changes. When you tweak your CSS, you'll see the results promptly.</p>
<p>This simulates the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Refresh"><code>Refresh</code> HTTP response header</a>, instructing browsers to reload the page after a certain number of seconds. You can change the duration if it's reloading too often for your taste; I find that 2 seconds is my sweet spot.</p>
<p>When you're all done, you should remove this tag so that the page doesn't reload for your users.</p>
<p>This isn't as powerful as hot reloading, to be sure. This primitive solution has a lot of drawbacks:</p>
<ul>
<li>It's usually slower than real hot reloading.</li>
<li>It can't intelligently swap out parts of your app, and instead brutally reloads the whole page.</li>
<li>It can mess with any state you've set up, such as scroll position.</li>
<li>It also refreshes the browser's developer tools, making them difficult to use.</li>
<li>If you use a screen reader, it's likely that this constant reloading will mess things up.</li>
</ul>
<p>Despite the disadvantages, this solution has one big plus: it's a single line of HTML that doesn't require any additional tooling. This minimal solution can be useful if you just need something simple. And that's hot.</p>
Submit to the Quirks of HTML
2024-12-10T00:00:00Z
https://htmhell.dev/adventcalendar/2024/10/
by Felix Hessenberger<br><p>It was on a cold February evening. I had been working on a client project, building an order item listānothing out of the ordinary. To adjust an itemās quantity, the user would open a popup form with a single input field, type a number, and hit enter.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Quantity</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>Everything worked as intended when suddenly, a new requirement came in: add another input field to also set a bulk quantity. I did what I was told, but to my horror, the form wouldnāt submit anymore with the new field displayed! š±</p>
<p>I checked the browser console for any JavaScript errors and double-checked my markup, but to no avail. A quick detour to <a href="https://stackoverflow.com/questions/4196681/form-not-submitting-when-pressing-enter">Stack Overflow</a> shed light on the situation: according to the <a href="https://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2">HTML 2.0 spec</a>, it's a feature, not a bug:</p>
<blockquote>When there is only one single-line text input field in a form, the user agent should accept Enter in that field as a request to submit the form.</blockquote>
<p>Some <a href="https://alanflavell.org.uk/www/formquestion.html">Web archeology</a> later, I found myself pretty deep down the rabbit hole about whether a form will submit on enter:</p>
<ul>
<li>Single-line <code>text</code> input: <strong>YES!</strong>, according to the discussion linked above, this was seen āas a convenient way to submit simple queriesā.</li>
<li>Other input types: <strong>YES!</strong>, <code>number</code>, <code>email</code>, <code>password</code>, <code>search</code>, <code>tel</code>, and <code>url</code> all are regarded as single-line text inputs; input types not recognized by the browser (e.g. <code>month</code> in Firefox at the time of writing), are treated like single-line text inputs.</li>
<li>One single-line text input together with any other fields that are <em>not</em> a single-line text input: <strong>YES!</strong>, you can add any other form elements including input types like <code>date</code> or <code>color</code>, even a <code>textarea</code>) and the form will still submit on enter (when the focus is on the text field).</li>
<li>A second single-line text input in a <em>different</em> form: <strong>YES!</strong>, itās not about how many inputs are on the page; each form is considered individually.</li>
<li>More than one single-line text input in the same form: <strong>NO!</strong> of course not! š
This is, according to the discussion mentioned above, aimed at āreducing the risk, on a complex form, of prematurely submitting it while trying to fill it in.ā.</li>
</ul>
<p><a href="https://codepen.io/felix-scale/pen/qEWEama">Try the above examples on CodePen</a></p>
<p>The reason I didnāt encounter this behavior before seems to be that Iāve never built a form with <em>only</em> one text field; even in a simple chat component, there typically is at least a submit button to send the message. And indeed, adding a submit button magically solves the problem:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Quantity</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Bulk quantity</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>submit<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>To extend on the list above:</p>
<ul>
<li><code><input type="submit"></code>: <strong>YES!</strong>, a submit button enables submit on enter in forms with more than one text input.</li>
<li><code><input type="submit" hidden></code>/<code><input type="submit" style="display: none;"></code>: <strong>YES!</strong>, the button may be hidden (which I did above to preserve the original appearance).</li>
<li><code><button></code>: <strong>YES!</strong>, the <code>button</code> element works as well. (<code>type="submit"</code> is the default for buttons within a form.)</li>
<li><code><button disabled></code>: <strong>NO!</strong>, disabling the submit input/button also disables submitting on enter.</li>
<li><code><button type="button"></code>: <strong>NO!</strong>, it may look the same, but now there is no way to submit the form anymore.</li>
</ul>
<p><a href="https://codepen.io/felix-scale/pen/vEBEWZO">Try the above examples on CodePen</a></p>
<p>All points listed in this article are the same across browsers; interestingly, the behavior of the submit button isnāt defined anywhere in the HTML specāit was initially just copied from Internet Explorer and Netscape for consistency (like discussed in this 23 year old (and still open) <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=104211">Firefox issue</a>). <em>However</em>, as outlined in <a href="https://jansensan.net/blog/enter-key-should-submit-form-currently-focus">this article</a>, the behavior can effectively be derived from combining the <a href="https://www.w3.org/WAI/standards-guidelines/wcag/">WCAG</a>ās recommendation that all forms should include a submit button and the HTML specās rule that the enter key should submit the form <em>implicitly</em>.</p>
<p>Bonus fact: The default keyboard on Android has a button that looks like an enter key, but it only submits the form when the <em>last text field</em> in the form is focusedāotherwise, it moves focus to the next text field. It doesnāt move the focus to any other form inputs.</p>
<p>This is a nice example that even after more than a decade of working with HTML, I still sometimes encounter unexpected quirks that, after a bit of digging, often turn out to be well-thought-out gems (<a href="https://htmhell.dev/adventcalendar/2023/15/">for the most part</a>).</p>
Native HTML light and dark color scheme switching
2024-12-09T00:00:00Z
https://htmhell.dev/adventcalendar/2024/9/
by Vadim Makeev<br><p>Itās getting dark early in Berlin in the winter. Itās not even close to evening, but my OS and all apps have already switched to dark mode. Well, not all of them, unfortunately. And thatās the thing: dark mode has become a quality-of-life feature for many users, and I often try to avoid using apps or websites that havenāt implemented it, especially in the evening. They literally hurt my eyes!</p>
<p>When it comes to color scheme implementations, they range from rather useless ones that require a page reload to more sensible ones that query the <code>prefers-color-scheme</code> media feature and apply changes in CSS on the fly:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">body</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffffff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #000000<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #000000<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #ffffff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<aside style="font-size: smaller">
<p>ā Iāll be using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting">native CSS nesting</a> in all demos throughout this article. It works in all modern browsers and makes code a bit more compact, especially when it comes to media queries. But if youāre not familiar with CSS nesting, you can use this handy <a href="https://lightningcss.dev/playground/#%7B%22minify%22%3Afalse%2C%22customMedia%22%3Atrue%2C%22cssModules%22%3Afalse%2C%22analyzeDependencies%22%3Afalse%2C%22targets%22%3A%7B%22chrome%22%3A6225920%7D%2C%22include%22%3A0%2C%22exclude%22%3A0%2C%22source%22%3A%22body%20%7B%5Cn%20%20background-color%3A%20%23000000%3B%5Cn%20%20color%3A%20%23ffffff%3B%5Cn%20%20%5Cn%20%20%40media%20(prefers-color-scheme%3A%20dark)%20%7B%5Cn%20%20%20%20background-color%3A%20%23000000%3B%5Cn%20%20%20%20color%3A%20%23ffffff%3B%5Cn%20%20%7D%5Cn%7D%22%2C%22visitorEnabled%22%3Afalse%2C%22visitor%22%3A%22%7B%5Cn%20%20Color(color)%20%7B%5Cn%20%20%20%20if%20(color.type%20%3D%3D%3D%20'rgb')%20%7B%5Cn%20%20%20%20%20%20color.g%20%3D%200%3B%5Cn%20%20%20%20%20%20return%20color%3B%5Cn%20%20%20%20%7D%5Cn%20%20%7D%5Cn%7D%22%2C%22unusedSymbols%22%3A%5B%5D%2C%22version%22%3A%22local%22%7D">Lightning CSS playground</a> to figure out how it looks without nesting.</p>
</aside>
<p>This approach is already a good start. But it only covers the simplest case and doesnāt allow users to choose a different color scheme for this specific website. Just like light color schemes hurt my eyes in the evening, many people are not comfortable with dark schemes or with particular ones that arenāt good for them. So, itās all about user choice.</p>
<p>Currently, thereās no way to directly override a userās OS preference if you want to offer a scheme selector on your page. Fortunately, in the CSS Media Queries Level 5 spec, thereās a <a href="https://drafts.csswg.org/mediaqueries-5/#auto-pref%E2%91%A0"><code>PreferenceManager</code> interface</a> that will solve this problem. Meanwhile, the most popular solution these days is to use JavaScript to set an attribute like <code><html data-scheme="dark"></code> reflecting the forced scheme and use it in CSS:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">body</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffffff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #000000<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">[data-scheme='dark'] &</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #000000<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #ffffff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>This approach always seemed hacky to me. However, if you know HTML well enough, there are a few much more convenient native options available.</p>
<h2 id="setting-the-css-color-scheme">Setting the CSS color-scheme</h2>
<p>If you ever fall into the dark scheme rabbit hole, the first thing youāll learn is the <code>color-scheme</code> CSS property. Itās essential for setting the scene for everything else. Most importantly, it turns on the browserās default dark scheme support. Which, unfortunately, browsers canāt enable by default for backward compatibility reasons.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:root</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color-scheme</span><span class="token punctuation">:</span> light dark<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The <code>light dark</code> value means that weāre choosing to support both light and dark schemes in our code. The property will be inherited down the document tree, and the browser will enable some default styling for built-in primitives when needed. Thank you, browser!</p>
<figure style="margin-bottom: 2.4rem">
<p><img src="https://htmhell.dev/adventcalendar/2024/9/images/color-scheme.png" alt="Two browser windows with the same page: one in the light scheme, the other in the dark scheme. The text on the page says: link is not a button! The link word is a link, the button word is a button. All page elements, including scrollbars, are perfectly aligned with the scheme." /></p>
<figcaption>
Default browser dark styles enabled by the <code>color-scheme: light dark</code> property.
</figcaption>
</figure>
<p>When switching your schemes, itās important to switch the value of this property, too: set <code>color-scheme: light</code> on the root along with light styles and the other way around for the dark ones. Remember this, itāll come in handy later.</p>
<p>And somewhere along these lines, youāll probably read that you can also set this mode right in HTML using the <code><meta></code> element. Why would you do that? Oh well, who knows? Maybe you donāt use CSS or whatever. So silly, right?</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>color-scheme<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>light dark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>It turns out that itās not just a flag for the browser but a tool you can use to force color schemes using JavaScript! But only if you use the fairly new <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark"><code>light-dark()</code></a> CSS function.</p>
<h2 id="switching-the-html-color-scheme">Switching the HTML color-scheme</h2>
<p>Remember the earlier example with media queries and <code>āprefers-color-scheme</code>?</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">body</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffffff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #000000<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #000000<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #ffffff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>You can also express the same idea like this:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">body</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">light-dark</span><span class="token punctuation">(</span>#ffffff<span class="token punctuation">,</span> #000000<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">light-dark</span><span class="token punctuation">(</span>#000000<span class="token punctuation">,</span> #ffffff<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<aside style="font-size: smaller">
<p>ā By the way, I use <code>--color-back</code> and <code>--color-text</code> variables in my demos. It makes it easier to set colors even in a small demo, let alone a bigger project. But to make things easier to read, I chose to set colors directly in code samples.</p>
</aside>
<p>Hereās the three-position switch I often use. Along with ālightā and ādarkā options that force a certain scheme, thereās also the āautoā option that gives the control over the color scheme back to the OS, selected by default.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</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>Color scheme switcher<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-pressed</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>light<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Light</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-pressed</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>light dark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Auto</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-pressed</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Dark</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>And to make it all work, a simple script that takes the buttonās value and sets it to the <code><meta name="color-scheme"></code>:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">const</span> colorScheme <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'meta[name=color-scheme]'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">const</span> switchButtons <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'button'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">switchButtons<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">button</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> button<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> currentButton <span class="token operator">=</span> button<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> switchButtons<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token parameter">button</span> <span class="token operator">=></span> button<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token string">'aria-pressed'</span><span class="token punctuation">,</span> button <span class="token operator">===</span> currentButton</span><br /><span class="highlight-line"> <span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> colorScheme<span class="token punctuation">.</span>content <span class="token operator">=</span> button<span class="token punctuation">.</span>value<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>As you can see, once we set <code>content="dark"</code> the browser switches to the last value in the <code>light-dark()</code> function and the other way around with the <code>light</code> one. This HTMLās <code>color-scheme</code> turned out not so silly after all!</p>
<p>To make it work properly, youāll need to decide where to store your global <code>color-scheme</code> value, so the script can force it. In this example, I chose to use the HTML one, so I removed the <code>color-scheme</code> property from CSS. But you can also keep it in CSS and force it via JavaScript like so:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">color-scheme</span><span class="token punctuation">:</span> dark</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span></code></pre>
<figure style="margin-bottom: 2.4rem">
<iframe src="https://htmhell.dev/adventcalendar/2024/9/color-scheme" style="
width: 100%;
height: 256px;
border: 6px solid #000;
"></iframe>
<figcaption>
Meta <code>color-scheme</code> switcher in action.
<a href="https://htmhell.dev/adventcalendar/2024/9/color-scheme" target="_blank">Open in a new tab</a>
</figcaption>
</figure>
<aside style="font-size: smaller">
<p>ā For a good UX, youāll also need a way to store user preference somewhere in <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage"><code>localStorage</code></a>, so the users wonāt have to switch it next time they visit or when opening a new tab. Iām sure you can figure it out on your own!</p>
</aside>
<p>One of the downsides of this approach is the browser support: the <code>light-dark()</code> CSS function has been available in all modern browsers since May 2024, which makes it ānewly availableā on the <a href="https://web-platform-dx.github.io/web-features/">Baseline</a> scale. It will become āwidely availableā only around November 2026 or 30 months later. You can transpile it for older browsers using <a href="https://lightningcss.dev/">Lightning CSS</a> or <a href="https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-light-dark-function">PostCSS plugin</a>, but make sure you check the output and test it in an older browser. It might be a bit tricky at times.</p>
<p>As for the other major downside, the <code>light-dark()</code> function accepts only colors for now. Trust me, I tried to use it with other values, and it didnāt work. So, whatās the problem? You might need to change not just colors but images or font properties that work better with the dark scheme.</p>
<p>In this case, thereās another HTML solution for you!</p>
<h2 id="linking-a-css-scheme">Linking a CSS scheme</h2>
<p>You all know the most common way of linking CSS to a page: itās a stylesheet linked from a file.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></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>index.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>But did you know that you can also set the <code>media</code> attribute to conditionally load and apply CSS based on user preferences? Sure you can! But first, youād need to split your files into light and dark styles:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span><br /><span class="highlight-line"> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>light.css<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-color-scheme: light)<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span><br /><span class="highlight-line"> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dark.css<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-color-scheme: dark)<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">></span></span></code></pre>
<p>It doesnāt look too convenient this way. A much better approach would be to split your styles into <em>three</em> parts! Yes, Iām serious. Hear me out!</p>
<ol>
<li>The main file, called <code>index.css</code>, will contain all your styles and use CSS variables for anything you need to change depending on a color scheme.</li>
<li>The <code>light.css</code> file will contain only CSS variables with values set to everything that makes sense for the light scheme.</li>
<li>The <code>dark.css</code>, you guessed it, will have everything dark.</li>
</ol>
<p>Remember to set the appropriate <code>color-scheme</code> property values in each color scheme file to help the browser: <code>color-scheme: light</code> and <code>color-scheme: dark</code>, respectfully.</p>
<p>Interestingly enough, the file that doesnāt fit user preferences will still be loaded by the browser but with lower priority. I went into much greater detail about this in my other ā<a href="https://pepelsbey.dev/articles/conditionally-adaptive/">CondiĀtionally adaptive CSS</a>ā article if youāre curious.</p>
<p>So, this three-file CSS architecture now has all the inconveniences and no benefits compared to the previously discussed solution with media queries. What now? Donāt you worry, it was just the first step. Now, to the switching.</p>
<h2 id="switching-linked-css-schemes">Switching linked CSS schemes</h2>
<p>When youāre linking your much simpler single-file styles, thereās something else youāre <em>implicitly</em> setting: the <code>media</code> attribute. If itās not <em>explicitly</em> set, it means that you want your styles to apply to all <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media#media_types">media types</a>. So, it defaults to <code>all</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></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>index.css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>all<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p><a href="https://www.filamentgroup.com/lab/load-css-simpler/">There are techniques</a> using <code>media="print"</code> for lazy-loading CSS, but since weāve learned that the <code>media</code> attribute can take not only media types but complex media queries, you can negate it as <code>not all</code> and it wonāt be applied to any media.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></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>index.css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>not all<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>But why would you need <em>not</em> to load your styles? Now you see where itās going. We can use it to switch between color schemes and force user preferences.</p>
<p>Letās take the same three-position switch from the previous example but change the auto buttonās value to <code>auto</code> to better match what it does. Our JavaScript function becomes a bit bigger, but the idea stays the same. The only difference this time is that weāre changing the <code>media</code> attributeās value:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">const</span> styleLight <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'link[rel=stylesheet][media*=prefers-color-scheme][media*=light]'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">const</span> styleDark <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'link[rel=stylesheet][media*=prefers-color-scheme][media*=dark]'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">switchScheme</span><span class="token punctuation">(</span><span class="token parameter">scheme</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">let</span> lightMedia<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">let</span> darkMedia<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>scheme <span class="token operator">===</span> <span class="token string">'auto'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> lightMedia <span class="token operator">=</span> <span class="token string">'(prefers-color-scheme: light)'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> darkMedia <span class="token operator">=</span> <span class="token string">'(prefers-color-scheme: dark)'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> lightMedia <span class="token operator">=</span> <span class="token punctuation">(</span>scheme <span class="token operator">===</span> <span class="token string">'light'</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">'all'</span> <span class="token operator">:</span> <span class="token string">'not all'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> darkMedia <span class="token operator">=</span> <span class="token punctuation">(</span>scheme <span class="token operator">===</span> <span class="token string">'dark'</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">'all'</span> <span class="token operator">:</span> <span class="token string">'not all'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> styleLight<span class="token punctuation">.</span>media <span class="token operator">=</span> lightMedia<span class="token punctuation">;</span></span><br /><span class="highlight-line"> styleDark<span class="token punctuation">.</span>media <span class="token operator">=</span> darkMedia<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>In short, if we force the dark scheme, the <code>dark.css</code> gets <code>media="all"</code> instead of the <code>prefers-color-scheme</code> and the <code>light.css</code> one gets <code>āmedia="not all"</code>, and the other way around for the light scheme. Once the user chooses the āautoā option, we stop forcing and restoring all previous <code>prefers-color-scheme</code> media values.</p>
<figure style="margin-bottom: 2.4rem">
<iframe src="https://htmhell.dev/adventcalendar/2024/9/link-media" style="
width: 100%;
height: 256px;
border: 6px solid #000;
"></iframe>
<figcaption>
Link <code>media</code> switcher in action.
<a href="https://htmhell.dev/adventcalendar/2024/9/link-media" target="_blank">Open in a new tab</a>
</figcaption>
</figure>
<p>Given that scheme files containing only variables are relatively small, and browsers download all CSS files anyway (only the priority differs), the switching happens seamlessly. You can check this method in action <a href="https://pepelsbey.dev/">on my website</a>. And if you happen to use Safari or Vivaldi browsers, you might notice something else changing while you switch the schemes.</p>
<h2 id="one-more-thing">One more thing</h2>
<p>Thereās the <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"><code>theme_color</code></a> key in the web manifest that sets the installed appās chrome color. You can also set it via HTML and even use the <code>media</code> attribute to apply different theme colors depending on the color scheme:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span><br /><span class="highlight-line"> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>theme-color<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#c1f07c<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-color-scheme: light)<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span><br /><span class="highlight-line"> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>theme-color<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#9874d3<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-color-scheme: dark)<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">></span></span></code></pre>
<p>I bet you saw that coming: you can also force the theme color while switching the color scheme using <code>all</code> and <code>not all</code> values via the same script. How cool is that?</p>
<figure style="margin-bottom: 2.4rem">
<p><img src="https://htmhell.dev/adventcalendar/2024/9/images/theme-color.png" alt="Two Safari windows: one in the light scheme, the other in the dark scheme. The light one has a lime top panel, the dark one has a violet top panel." /></p>
<figcaption>
Different top panel colors using the <code>theme-color</code> meta element.
</figcaption>
</figure>
<h2 id="homework">Homework</h2>
<p>As all of us did, I learned most of what I know from other peopleās posts and articles. I want to say a special thank you to Thomas Steiner for his early articles on color scheme switching: <a href="https://web.dev/articles/prefers-color-scheme#dark-mode-but-add-an-opt-out">Prefers-color-scheme: Hello darkness, my old friend</a> and <a href="https://web.dev/articles/color-scheme">Improved dark mode default styling with the color-scheme</a>. Please also have a look at Darin Senneffās <a href="https://www.darins.page/articles/progressively-enhanced-dark-mode">Progressively-enhanced dark mode</a> and Sara Joyās <a href="https://css-tricks.com/come-to-the-light-dark-side/">Come to the light-dark() side</a> articles for a different perspective on the matter. And letās dream about a better future for color scheme switching while reading Bramusā <a href="https://www.bram.us/2024/04/13/what-if-you-had-real-control-over-light-mode-dark-mode-on-a-per-site-basis/">What if you had real control over light mode / dark mode on a per-site basis?</a> article.</p>
Past HTML, Future HTML?
2024-12-08T00:00:00Z
https://htmhell.dev/adventcalendar/2024/8/
by Jens Oliver Meiert<br><p>Consider the following HTML document:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">HTML</span> <span class="token name">PUBLIC</span> <span class="token string">"-//W3C//DTD HTML 1996-01//EN"</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>Author<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>P</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>P</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>H2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>H2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>P</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>UL</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>LI</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>LI</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>LI</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>UL</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>P</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ⦠--></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<p>You will notice a few things:</p>
<ul>
<li>The unusual doctype</li>
<li>The inconsistencies in element case (most notably, <code><p></code> vs. <code><P></code>)</li>
<li>The inconsistencies in omitting optional tags (like dropping <code></p></code> and <code></li></code> end tags, but keeping <code></body></code> and <code></html></code>)</li>
</ul>
<p>Yet in some respects, this document structure is better than the code we find on many modern websites (and that is sites, not even apps).</p>
<p>Why? Whatās to like about this kind of code?</p>
<ol>
<li>
<p><em>Itās valid.</em> If you run this and the respective original pageās code through an HTML conformance checker like <a href="https://validator.w3.org/">the W3C markup validator</a>, the code will validate. This is basic <em>professional</em> work <a href="https://meiert.com/en/blog/html-conformance-2024/">that we donāt see often anymore</a>.</p>
</li>
<li>
<p><em>Itās focused.</em> Thereās close to no superfluous code. While thereās a little bit more <a href="https://meiert.com/en/blog/optional-html/">optional markup</a> to be removed, as well as perhaps the <code>Author</code> class, one might rather <em>add</em> code, like the style sheet reference thatās conspicuously missing.</p>
</li>
<li>
<p><em>Itās⦠open-minded.</em> These inconsistencies, they may rub us, and we likely prefer a consistent formatting. But that shouldnāt distract from the fact that the code is valid (which includes uppercase tag names), and that it represents legitimate ways of writing HTML (which in this case means <a href="https://css-tricks.com/write-html-the-html-way-not-the-xhtml-way/">HTMLāHTML, and not XHTMLāHTML</a>).</p>
</li>
</ol>
<p>So where is this code from? Itās from <a href="https://en.wikipedia.org/wiki/T._V._Raman">T.V. Ramanās</a> documentation on <a href="https://www.w3.org/Style/CSS/Speech/speech.html">style sheets for spoken renderings</a>, published on February 12, <em>1996</em>. The <a href="https://frontenddogma.com/topics/1990s/">1990s</a> and <a href="https://frontenddogma.com/topics/2000s/">2000s</a> are behind us in many areas, and yet in some, they may still be ahead:</p>
<p>We could learn from this code and take it into the future, because XHTMLāHTML may have long been dead if it wasnāt kept on eternal life support by frameworks and tools that do not use any of the features that HTML (and that is, donāt mind the repetition, <a href="https://css-tricks.com/write-html-the-html-way-not-the-xhtml-way/#aa-the-html-way-of-writing-html">HTMLāHTML</a>) offers us. The future of HTMLāit could embrace more of the HTML of the past.</p>
Almost, but not quite, entirely unlike...
2024-12-07T00:00:00Z
https://htmhell.dev/adventcalendar/2024/7/
by LƩonie Watson<br><blockquote class="blockquote-no-quotes highlight">Can you give me the HTML for an accessible button please?</blockquote>
<p>It was a simple enough question. Or it would have been, had I been asking another person. As it was, I was asking ChatGPT, and so of course there was nothing simple about it.</p>
<p>For the briefest of moments, I caught myself thinking even ChatGPT couldn't get this wrong. The <code><button></code> element is probably the most talked about HTML element in accessibility. Literally all you have to do is use it, give it an accessible name, attach some JavaScript to get it to do something if it isn't part of a form, and you don't even need to think about keyboard accessibility because the browser takes care of all that for you.</p>
<p>Then reality kicked me in the shins and told me to snap out of it, and, as if to prove reality's point, ChatGPT burbled:</p>
<blockquote class="blockquote-no-quotes highlight">Absolutely! Hereās an accessible button in its simplest form, following best practices to ensure compatibility with screen readers and keyboard navigation.</blockquote>
<p>OK, I thought to myself, that doesn't sound too terrible. I sat up a little straighter and read on as ChatGPT presented me with:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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>accessible-button<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>Submit form<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Submit</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>I stared at my screen for a while and wondered where it had all gone wrong.</p>
<p>At first glance it looks like a perfectly reasonable bit of HTML. Well, except it isn't just HTML, but we'll come back to that in a minute.</p>
<p>It's clearly supposed to be a button for submitting a form. It says so right there, twice in fact. So why then does it have <code>type="button"</code> and not <code>type="submit"</code>?</p>
<p>When the <code>type</code> attribute has a value of "submit" it causes the browser to submit form data. When the <code>type</code> attribute has a value of "button", it tells the browser to do nothing when the button is pressed - but a bit of JavaScript to listen for the button's events is all that's needed for the button to do something useful when pressed.</p>
<p>I checked my question. No, definitely no mention of a submit button. Not even any mention of what I wanted the button for. "Fine" I thought. I'll ask it what it thinks it's up to:</p>
<blockquote class="blockquote-no-quotes highlight">Why do I need the type attribute?</blockquote>
<p>Without missing a beat, ChatGPT told me:</p>
<blockquote class="blockquote-no-quotes highlight">The <code>type</code> attribute in the <code><button></code> element is important because it determines how the button behaves, especially in the context of forms. Without specifying the type, the browser applies a default behavior that might not align with your intentions.</blockquote>
<p>It went on:</p>
<blockquote class="blockquote-no-quotes highlight"><ol><li><code>type="submit"</code> (default behavior):
<ul>
<li>
<p>If you donāt explicitly set a type, the button defaults to submit. This means that if the button is inside a form, clicking it will submit the form.</p>
</li>
<li>
<p>Use case: When you want the button to submit the form data to the server or trigger a form submission.</p>
</li>
</ul>
</li>
<li><code>type="button"</code>:
<ul>
<li>This type ensures that the button does not submit the form when clicked. Instead, it is just a clickable button that you can use for custom actions (e.g., toggling content, triggering JavaScript functions).</li>
<li>Use case: When you want the button to perform an action other than submitting a form.</li>
</ul>
</li>
</ol>
[ā¦]
</blockquote>
<p>I set down my teacup and put my face in my hands. <em>āRightā</em> I said, finally looking up and channelling my inner <a href="https://youtu.be/gy7xPCImMXk?t=46">Basil Fawlty</a>:</p>
<blockquote class="blockquote-no-quotes highlight">So, why did you give me an example of a submit button with a type attribute set to "button"?</blockquote>
<p>There was an almost infinitesimal pause before ChatGPT chirped:</p>
<blockquote class="blockquote-no-quotes highlight">You're absolutely right to point out the inconsistency! In the example I gave, the button was labelled "Submit" and intended to suggest a submission action, but I set the type="button", which prevents it from submitting the form. This could definitely be confusing since "Submit" usually implies that the button should submit a form.</blockquote>
<p>I closed my eyes and contemplated my life choices.</p>
<p>When I opened them again, nothing much seemed to have changed, so I took a deep breath and in the kind of slow and careful voice that hasn't been heard since <a href="https://www.youtube.com/watch?v=ZXhHFgDRNBQ">Joyce Grenfell in the Nursery School sketch</a>, I asked:</p>
<blockquote class="blockquote-no-quotes highlight">OK, never mind the type attribute, why did you use the aria-label attribute when the <code><button></code> has a perfectly good accessible name already?</blockquote>
<p>I narrowed my eyes as ChatGPT brightly informed me:</p>
<blockquote class="blockquote-no-quotes highlight">You're absolutely right again! The `aria-label` attribute is not needed when the button already has a clear, visible text label. The visible text content of a <code><button></code> element is automatically accessible to screen readers and other assistive technologies, so there's no need for `aria-label` unless you want to provide an alternative or additional description that's not visible.</blockquote>
<p>Suddenly I knew exactly how Arthur Dent felt, dealing with the <a href="https://www.youtube.com/watch?v=eAswvg60FnY">Nutrimatic Drinks Dispenser</a> in The Hitchhiker's Guide to the Galaxy. Just like the cup of tea it dispensed to Arthur, the code provided by ChatGPT was, to paraphrase, almost, but not quite, entirely unlike the HTML for an accessible button.</p>
<p>If ChatGPt hadn't been trained by indiscriminately hoovering up everything on the web, and if humans hadn't spent the last 30 years creating so many inaccessible buttons for it to hoover up in the first place, then the answer it gave me should have gone a little something like this:</p>
<blockquote class="blockquote-no-quotes highlight">To create an accessible button in HTML (outside of a form), you can use the <code><button></code> element like this:</blockquote>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Accessible name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<blockquote class="blockquote-no-quotes highlight">This element works well with a keyboard: you can focus it using the Tab key and activate it with either the Spacebar or Enter key. When triggered this way, the browser will run any JavaScript tied to the buttonās click event. Just make sure to style the button so that its appearance changes when it gets focus, providing clear feedback to users.
<br />
For users relying on screen readers or other assistive technologies, the button element is automatically recognized as a button. Just remember to replace "Accessible name" with a descriptive label that fits your buttonās functionāsuch as "Search" or "Play."</blockquote>
aria-labelledby = self
2024-12-06T00:00:00Z
https://htmhell.dev/adventcalendar/2024/6/
by Weston Thayer<br><p>An accessible name is how UI components are identified to assistive tech. Having a good accessible name is important. If not, negative effects may include screen reader users missing out on vital information, voice control users struggling to interact, and any number of other issues with the assistive technologies that rely on it.</p>
<p>By default, accessible name is computed from an element's own contents. That's part of the <a href="https://www.w3.org/TR/accname-1.1/">spec'd algorithm</a> that browsers implement.</p>
<figure class="u-mb">
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/updates<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Latest updates</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<figcaption>A link with the accessible name "Latest updates".</figcaption>
</figure>
<p>There are many ways to change that behavior. The <code>aria-labelledby</code> attribute is one which can be used to point the algorithm somewhere else in the DOM. Or not.</p>
<figure class="u-mb">
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Latest updates<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/updates<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<figcaption>A link with the accessible name "Latest updates".</figcaption>
</figure>
<p>What happens if <code>aria-labelledby</code> points within?</p>
<h2 id="purgatory">Purgatory</h2>
<figure class="u-mb">
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/updates<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>contents<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>contents<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Latest updates<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<figcaption>A link, still with the accessible name "Latest updates".</figcaption>
</figure>
<h2 id="testing">Testing</h2>
<p>The above snippet should be equivalent to <code><a href="/updates"><span>Latest updates</span></a></code>. Are there any issues besides being unnecessarily verbose code (and breaking the <a href="https://www.w3.org/TR/using-aria/#rule1">first rule of ARIA</a>)?</p>
<p>Technically, the <a href="https://www.w3.org/TR/wai-aria/#aria-labelledby">WAI-ARIA spec</a> does not limit the DOM location of the element you point to.</p>
<p>I also checked how each major browser computes the element's accessible name via their developer tools (for example, <a href="https://developer.chrome.com/docs/devtools/accessibility/reference#computed">Chrome's DevTools</a>):</p>
<ul>
<li>Chrome (129) ā
</li>
<li>Edge (129) ā
</li>
<li>Firefox (131) ā
</li>
<li>Safari (18.1) ā
</li>
</ul>
<p>I did only test those browsers on macOS, and I didn't test with different assistive technologies. But I trust that if the browser's tools show an accessible name of <code>"Latest updates"</code>, screen readers and voice control will have that value too.</p>
<h2 id="judgement">Judgement</h2>
<p>As is, I'd feel like I was nitpicking by requesting the unnecessary <code>aria-labelledby</code> to be removed.</p>
<p>But imagine this is in a large codebase with many developers working on it. Consider how easy it would be for someone tasked with adding a notification badge to the link, for example, to miss the implications of <code>aria-labelledby</code>:</p>
<figure class="u-mb">
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/updates<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>contents<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>contents<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Latest updates<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>notification-badge<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ updates.length }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<figcaption>A link, still with the accessible name "Latest updates".</figcaption>
</figure>
<p>Now the count of updates is <strong>not</strong> part of the link's accessible name. Screen readers will not announce it. macOS Voice Control users will not be able to say "click Latest updates three" (you would need to omit the "three", macOS Voice Control requires you to say the accessible name exactly). <a href="https://support.apple.com/guide/mac-help/view-a-larger-version-text-reading-typing-mchlb203bc78/mac">macOS Hover Text</a> will miss the count as well.</p>
<img alt="The mouse hovering the link with 'Latest updates' in black and underlined, with the number 3 on a red circle to the right. macOS Hover Text's overlay is below with only 'Latest updates' in large type, blue and underlined." src="https://htmhell.dev/adventcalendar/2024/6/hovertext.png" width="872" height="252" loading="lazy" />
<p>And it's a failure of <a href="https://www.w3.org/TR/WCAG22/#label-in-name">WCAG SC 2.5.3 Label in Name</a> to boot.</p>
<p>So put this link on a heavenly path and drop the <code>aria-labelledby</code>! Your future self will thank you.</p>
<h2 id="caveats">Caveats</h2>
<p>There are good reasons to point <code>aria-labelledby</code> within an element. For example, to <a href="https://www.w3.org/TR/wai-aria/#example-27">give an accessible name to a region</a> based off of the heading within it.</p>
<figure class="u-mb">
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</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>heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Save changes?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Do you want to save your work?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Cancel<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Save<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<figcaption>A dialog with the accessible name "Save changes?".</figcaption>
</figure>
<p>I tend to only be wary when the element with <code>aria-labelledby</code> is an interactive control (link, button, etc).</p>
Forced Colors Mode Futility
2024-12-05T00:00:00Z
https://htmhell.dev/adventcalendar/2024/5/
by Matthias Zƶchling<br><style>figure{margin-bottom:2.4rem}figure img{aspect-ratio:4;border: 6px solid #000}figcaption,sup,.highlight,section:has(#resources) span,section:has(#fns) ol{font-size:87.5%}.highlight{margin-left:1.5em;padding:1rem;text-wrap:balance}.highlight::before{content:attr(data-icon);content:attr(data-icon) / '';position:absolute;margin-left:calc(-1.5em - 1rem);filter:grayscale()}.highlight em{font-style:inherit;text-transform:uppercase}sup{position:relative;top:-.5em;vertical-align:baseline}</style>
<p>Are you aware of <a href="https://blogs.windows.com/msedgedev/2020/09/17/styling-for-windows-high-contrast-with-new-standards-for-forced-colors/">Forced Colors Mode</a>? If not, there are some <a href="https://htmhell.dev/adventcalendar/2024/5/#resources">resources</a> at the end. If so, did you also know that this accessibility feature can be used as an entry-level debugging tool?</p>
<figure><img src="https://htmhell.dev/adventcalendar/2024/5/htmhell-goes-fcm.webp" alt="Comparing HTMHell.dev: Three screenshots taken in Microsoft Edge browser. Although subtle tweaks could be made, overall the site works really well in Forced Colors Mode." />
<figcaption>From left to right: The HTMHell Advent Calendar 2024 in its intended theme, when viewed in Windows 11 contrast theme āNight Skyā, and āDesertā.</figcaption>
</figure>
<p class="highlight" data-icon="ā¹ļø"><strong>Note:</strong> Iāve created a <a href="https://codepen.io/cssence/pen/BaXMNyv">CodePen with all the upcoming examples,</a> so you can follow along.</p>
<h2 id="premise">Premise</h2>
<p>When the forced colors feature is turned on, colors will be replaced with <a href="https://adrianroselli.com/2021/02/whcm-and-system-colors.html#CSS4">CSS System Colors</a>. Elements like buttons and links get special colors assigned, so wherever improper elements are used, things will fall out of place.</p>
<h2 id="bad-advice">Bad advice</h2>
<p>Okay, surely thereās a way around this, in true HTMHell.dev spiritā½</p>
<p class="highlight" data-icon="ā ļø"><strong>Warning:</strong> Sarcasm ahead! <em>Donāt try this at home!</em></p>
<p>Letās use a <code><div></code> to create a button, and <code><u></code> to create a link. By doing the latter, our links are already underlined, but we avoid this pesky menu on right-click that feels out of place in our carefully crafted user interface.<sup><a id="ref-fn-1" href="https://htmhell.dev/adventcalendar/2024/5/#fn-1" role="doc-noteref" aria-label="Footnote #1">[1]</a></sup></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>āButtonā<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>u</span> <span class="token attr-name">data-href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://example.com/very-bad<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>āLinkā<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>u</span><span class="token punctuation">></span></span></span></code></pre>
<p>Thanks to the <code>data-href</code> on the link we know where to go. And yes, this means a little JavaScript will be needed to actually go there. Most likely we anyhow have a Single Page Application, so weāll stay put.</p>
<p>Now all that is left to be done is style our buttons and add color to our links.<sup><a id="ref-fn-2" href="https://htmhell.dev/adventcalendar/2024/5/#fn-2" role="doc-noteref" aria-label="Footnote #2">[2]</a></sup></p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.button</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> .125rem solid<span class="token punctuation">;</span> <span class="token comment">/* etc. */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">u</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">light-dark</span><span class="token punctuation">(</span><span class="token function">oklch</span><span class="token punctuation">(</span>0.43 0.3 264.05<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">oklch</span><span class="token punctuation">(</span>0.69 0.17 281.16<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>All good? Sadly no, Iām sure the accessibility police will soon be all over the place.</p>
<p>Fine, letās make our elements interactive. For brevity, Iām not gonna talk about all the JavaScript required, but rest assured to make things accessible weāll need a metric fuckton of it.<sup><a id="ref-fn-3" href="https://htmhell.dev/adventcalendar/2024/5/#fn-3" role="doc-noteref" aria-label="Footnote #3">[3]</a></sup></p>
<p>To fix the HTML, I asked artifical āintelligenceā, and several prompts later I learned that we need to sprinkle some ARIA roles and tabindexes on top.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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">class</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>āButtonā<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>u</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>link<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">data-href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://example.com/bad<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>āLinkā<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>u</span><span class="token punctuation">></span></span></span></code></pre>
<p>Weāre good? Again, no, this is where <strong>Forced Colors Mode</strong> comes in. <a href="https://htmhell.dev/adventcalendar/2022/2/">No amount of ARIA</a> turns our elements into their semantic counterparts. As such, they all will be displayed in plain CanvasText color.</p>
<p>Alright, letās manually add those colors, but inside a media query, so they wonāt leak into our regular theme.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><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></span><br /><span class="highlight-line"> <span class="token selector">.button</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> ButtonText<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> ButtonFace<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">u</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> LinkText<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>I was about to ask āIs this it?ā, but I just realized we need a database to keep track of all the links that have already been clicked, so we can visually indicate them as <code>.visited</code>, ... ā Iām gonna stop myself right there.</p>
<h2 id="good-advice">Good advice</h2>
<p>Enough of the Sisyphean labor. Letās do the following instead.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Button<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com/good<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Link<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>For styling, all you have to do is inherit the font for buttons, which is what your <a href="https://www.joshwcomeau.com/css/custom-css-reset/#six-inherit-fonts-for-form-controls-7">CSS reset</a> might do anyhow for all form controls.</p>
<p>In Forced Colors Mode, things just work, buttons and links will be shown in proper CSS system colors. <strong>Semantics for the win!</strong><sup><a id="ref-fn-4" href="https://htmhell.dev/adventcalendar/2024/5/#fn-4" role="doc-noteref" aria-label="Footnote #4">[4]</a></sup></p>
<figure><img src="https://htmhell.dev/adventcalendar/2024/5/a-div-is-not-a-button-even-in-fcm.webp" alt="While we eventually can make our fake elements look like the real deal even in Forced Colors Mode, we should avoid all that work and use proper elements instead." />
<figcaption>The <a href="https://codepen.io/cssence/pen/BaXMNyv">accompanying CodePen</a> in āNight Skyā (top) and āDesertā (bottom) contrast theme.</figcaption>
</figure>
<p>So do we have an entry-level debugging tool? Well, unless someone goes all the way like we did in the last step of our bad example (highly unlikely!), in Forced Colors Mode you donāt need to be a developer to recognize real buttons and links. Unless the theme prevents it: Buttons are yellow and regular text is white in the āNight Skyā theme, but in āDesertā the difference is way too subtle. No need to stick to the defaults tough, you can create your own theme instead.</p>
<h2 id="full-disclosure">Full disclosure</h2>
<p>I didnāt need to write this article, because common sense exists, right? Then again, thereās a reason why HTMHell.dev exists. (<abbr title="By the way">BTW</abbr>, thanks for having me, itās been an honor to be part of the <a href="https://htmhell.dev/adventcalendar/2024/">2024 Advent Calendar</a> among all those talented people.)</p>
<p>Initially I wrote something else, but it ended up being too long for a calendar entry. <em>The original article</em> is now <a href="https://cssence.com/2024/forced-colors-mode-strategies/">available on my blog</a>. If you are up for <strong>actual Forced Colors Mode advice,</strong> may I suggest you read it, and then consider joining my movement to make December <q cite="https://cssence.com/2024/forced-colors-mode-strategies/">the least colorful time of the year</q>.</p>
<section aria-labelledby="resources">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://polypane.app/blog/forced-colors-explained-a-practical-guide/">Forced colors explained: A practical guide</a>, <span>18min read by Kilian Valkhof</span></li>
<li><a href="https://youtu.be/yYGLEy7CiT0">Forced colors and CSS</a>, <span>16min video by Kevin Powell</span></li>
<li><a href="https://www.smashingmagazine.com/2022/03/windows-high-contrast-colors-mode-css-custom-properties/#styling-the-modal-for-forced-colors-mode">Windows High Contrast Mode, Forced Colors Mode And CSS Custom Properties</a>, <span>11min read by Eric W. Bailey</span></li>
<li><a href="https://cssence.com/2024/forced-colors-mode-strategies/">Forced Colors Mode Strategies</a>, <span>14min read by yours truly</span></li>
</ul>
</section>
<section aria-labelledby="fns" class="section">
<h2 id="fns">Footnotes</h2>
<ol>
<li id="fn-1">Some even argue underlined links go against an app-like feel, but letās not go there. <a href="https://htmhell.dev/adventcalendar/2024/5/#ref-fn-1" role="doc-backlink" aria-label="Back to article">ā©ļø</a></li>
<li id="fn-2">Look at those CSS functions our Fancy Pants author uses to add color to a link. <a href="https://htmhell.dev/adventcalendar/2024/5/#ref-fn-2" role="doc-backlink" aria-label="Back to article">ā©ļø</a></li>
<li id="fn-3">And itās very likely that weāll still fail to do so. <a href="https://htmhell.dev/adventcalendar/2024/5/#ref-fn-3" role="doc-backlink" aria-label="Back to article">ā©ļø</a></li>
<li id="fn-4">Mmm š¤¤, semantics! If you wanna dig deeper, hereās a <a href="https://codepen.io/cssence/pen/RwXmEYN">CodePen with HTML elements</a> (unstyled, and with CSS system colors for reference) for you to view in Forced Colors Mode. Bonus points if you explore the differences between browers. <a href="https://htmhell.dev/adventcalendar/2024/5/#ref-fn-4" role="doc-backlink" aria-label="Back to article">ā©ļø</a></li>
</ol>
</section>
Control the Viewport Resize Behavior on mobile with `interactive-widget`
2024-12-04T00:00:00Z
https://htmhell.dev/adventcalendar/2024/4/
by Bramus<br><style>
figcaption {
font-size: 0.8em;
text-align: center;
}
</style>
<p><a href="https://github.com/web-platform-tests/interop-2022-viewport/blob/main/explainers/viewport-units.md">Viewports units</a> on mobile have been a problem for a long time. Using <code>100vh</code> is most likely not exactly what you initially expected it to be. To fix this, the CSS Working Group came up with more units over time for you to use. The <a href="https://web.dev/blog/viewport-units">dynamic viewport units</a> got introduced, which include <code>svh</code> and <code>lvh</code> which represent 1% of the small and large viewport height respectively.</p>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2024/4/viewports-01.jpg" width="4001" height="2550" alt="" />
<figcaption>Two mobile browser visualizations positioned next to each other. One has an element sized to be <code>100svh</code> (left, green) and the other <code>100lvh</code> (right, blue). The blue dashed outline represents the Layout Viewport.</figcaption>
</figure>
<p>While these units are <em>fairly</em> interoperable at the time of writing ā there are still some interop issues, mainly related to webviews ā there is one big gripe that a lot of people have with it: when the Virtual Keyboard gets shown, these units do not take the presence of that Virtual Keyboard into account. Depending on what you are building, you might want to have these units get resized when the Virtual Keyboard is shown.</p>
<h2 id="the-default-viewport-resize-behavior">The default viewport resize behavior</h2>
<p>When the Virtual Keyboard gets shown on mobile, it gets laid over the content. As a result, the <a href="https://github.com/web-platform-tests/interop-2022-viewport/blob/main/explainers/visual-viewport.md">Visual Viewport</a> gets resized but the <a href="https://github.com/web-platform-tests/interop-2022-viewport/blob/main/explainers/layout-viewport.md">Layout Viewport</a> - the one that is used to position out <code>position: fixed</code> elements ā remains unchanged.</p>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2024/4/viewports-02.jpg" width="4001" height="2550" alt="" />
<figcaption>Visualization of a mobile browser with the virtual keyboard opened. The elements that have a height of <code>100svh</code> and <code>100lvh</code> are not affected by the virtual keyboardās presence.</figcaption>
</figure>
<p>Because the Layout Viewport does not change, the Viewport Units also donāt change. The only thing that changes is the size of the Visual Viewport, wich represents only the visual part that you see on screen.</p>
<p><em>Note: Prior to Chrome 108, Chrome on Android used to resize the Layout Viewport when the Virtual Keyboard was shown. To align with MobileSafari, Chrome adjusted its behavior to resize only the Visual Viewport instead. Firefox did the same with the release of Firefox 132.</em></p>
<p>What sometimes also happens is that browsers shift the Layout Viewport upwards (or the Visual Viewport downwards depending on how you look at it) in order to keep the focused input at the center of the screen.</p>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2024/4/viewports-03.jpg" width="4001" height="2550" alt="" />
<figcaption>Visualization of the Visual Viewport <em>(orange dotted outline)</em> on a page with the Virtual Keyboard shown. On the right, the Layout Viewport <em>(blue dashed outline)</em> got shifted up so that the focused input is centered on the screen.</figcaption>
</figure>
<h2 id="intermezzo">Intermezzo</h2>
<p>If you want to know all the details about Viewports, <a href="https://www.youtube.com/watch?v=xl9R8aTOW_I">check out this HTTP 203 episode</a>:</p>
<div class="video">
<iframe width="560" height="315" src="https://www.youtube.com/embed/xl9R8aTOW_I?si=5jEnH9fWKF3im80C" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>
</div>
<p>The relevant part for you to know here is that the size of the <a href="https://github.com/web-platform-tests/interop-2022-viewport/blob/main/explainers/icb.md">Initial Containing Block (ICB)</a> is based on the Layout Viewport ā more specifically the Small Layout Viewport ā and that the Viewport Units are based on the size of the ICB.</p>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2024/4/viewports-04.jpg" width="4001" height="2550" alt="" />
<figcaption>Visualization of the Initial Containing Block or ICB for short <em>(red dashed outline)</em>. Itās size is taken from the Small Layout Viewport.</figcaption>
</figure>
<p>If you prefer reading, there are also <a href="https://github.com/web-platform-tests/interop-2022-viewport/blob/main/explainers/README.md">a bunch of explainers</a> for you to review.</p>
<h2 id="the-interactive-widget-keyword-in-the-viewport-meta-tag">The <code>interactive-widget</code> keyword in the viewport meta tag</h2>
<p>To control the viewport's size and shape there is the viewport meta tag. Most likely you have used the following before:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>In the <code>content</code> there are two properties listed, each with their own value:</p>
<ul>
<li><code>width</code>: Sizes the width of the ICB. The value <code>device-width</code> indicates that it should be size in relation to the deviceās width.</li>
<li><code>initial-scale</code>: Sets the zoom level when the page is first loaded. It is set to <code>1</code>, which is also the default <em>(in most cases)</em>.</li>
</ul>
<p>Other keywords that you might have used before include <code>height</code>, <code>minimum-scale</code>, <code>maximum-scale</code>, and <code>user-scalable</code>.</p>
<p>A fairly new keyword that you can use in the viewport meta tag is <code>interactive-widget</code>. It allows you to specify how the various viewports should behave when <em>āinterative widgetsā</em> such as the Virtual Keyboard get shown.</p>
<ul>
<li><code>resizes-visual</code>: Resize only the Visual Viewport but not the Layout Viewport.</li>
<li><code>resizes-content</code>: Resize both the Visual Viewport and Layout Viewport.</li>
<li><code>overlays-content</code>: Do not resize any viewport. This is similar to using the <a href="http://brm.us/virtual-keyboard">Virtual Keyboard API</a> with <code>overlaysContent</code> set to <code>true</code>.</li>
</ul>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2024/4/viewports-05.jpg" width="4001" height="2550" alt="" />
<figcaption>The impact of the various values for <code>interactive-widget</code>. The orange dotted box is the Visual Viewport. The blue one is the Layout Viewport. Both resize differently depending on the value of <code>interactive-widget</code>.</figcaption>
</figure>
<p>By setting <code>interactive-widget</code> to <code>resizes-content</code>, you can have the Layout Viewport resize. As a result the ICB will also resize, and therefore the Viewport units will also yield different values.</p>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2024/4/viewports-06.jpg" width="4001" height="2550" alt="" />
<figcaption>Visualization of the impact of setting <code>interactive-widget</code> to <code>resizes-content</code>. When thatās the case and the Virtual Keyboard is shown, the ICB resizes and the Viewport Units get adjusted.</figcaption>
</figure>
<p><code>interactive-widget</code> is supported in Chrome 108+ and Firefox 132+. If you would like to see this become available in WebKit/Safari, please go give <a href="https://github.com/WebKit/standards-positions/issues/65">https://github.com/WebKit/standards-positions/issues/65</a> a thumbs up.</p>
<h2 id="summary">Summary</h2>
<p>Use the <code>interactive-widget</code> keyword in the viewport meta tag to control the viewport resize behavior when the Virtual Keyboard gets shown. By default the Visual Viewport resizes. To also have the Layout Viewport resize, set the value of <code>interactive-widget</code> to <code>resizes-content</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0, interactive-widget=resizes-content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>To not resize any of those viewports, set its value to <code>overlays-content</code>.</p>
<p>The <code>interactive-widget</code> keyword is supported in Chrome 108+ and Firefox 132+.</p>
<h2 id="further-reading">Further Reading</h2>
<ul>
<li><a href="https://web.dev/blog/viewport-units">The large, small, and dynamic viewport units</a></li>
<li><a href="https://github.com/bramus/viewport-resize-behavior/blob/main/explainer.md">Viewport vs Virtual Keyboard Resize Behavior</a></li>
<li><a href="https://developer.chrome.com/blog/viewport-resize-behavior">Prepare for viewport resize behavior changes coming to Chrome on Android</a></li>
<li><a href="https://www.youtube.com/watch?v=xl9R8aTOW_I">Itās Viewports all the way down | HTTP 203</a></li>
</ul>
Smooth Multi-Page Experiences with Just a Few Lines of CSS
2024-12-03T00:00:00Z
https://htmhell.dev/adventcalendar/2024/3/
by John Allsopp<br><p>A single line of CSS can enable slick multi-page transitions for web applications (and web sites for those who maintain there's a difference), opening up new possibilities for web app architectures, and website experiences. So letās take a look at <a href="https://www.w3.org/TR/css-view-transitions-1/">View Transitions</a>, why we might want them, and how to get started with just that single line of CSS.</p>
<h2 id="the-webs-long-legacy-of-native-app-envy">The Web's Long Legacy of Native App Envy</h2>
<p>The launch of the iPhone coincided with (and likely ignited) a resurgence of the web. After years of stagnation under the dominance of Internet Explorer, competition and innovation were rekindled by Safari for Mac and Firefox. The iPhoneās killer feature (hard as it might be to believe now) was the āfull Safariā on a mobile device.</p>
<p>The Safari team introduced native CSS features like gradients, rounded corners, web fonts, transitions, animations, and transforms. Slick interactive experiences were now a reality on the web.</p>
<p>Then native iPhone apps arrivedāwith their smooth, animated state transitions between views, panels and widgets sliding in and out, and satisfying, physics-driven responses to user interactions. The web's traditional multi-page architecture was no match; moving from one page to another was clunky, with screens going blank as new pages loaded over sluggish 3G networks. Rendering engines struggled on 2010-era chips.</p>
<p>The common refrain was that native apps were inherently better than web apps. In terms of UI slickness, it was hard to argue otherwise.</p>
<p>The nadir perhaps came in August 2010, when the then-bible of the technology industry, Wired, proclaimed on its front cover:</p>
<h3 id="the-web-is-dead-long-live-the-internet">The Web Is Dead. Long Live the Internet</h3>
<blockquote class="code-label">Two decades after its inception, the World Wide Web has been eclipsed by Skype, Netflix, peer-to-peer, and a quarter-million other apps.</blockquote>
<cite>
<p><a href="https://www.wired.com/2010/08/ff-webrip/">The Web Is Dead. Long Live the Internet</a></p>
</cite>
<p>The problem wasnāt just the lack of engaging transitions. Web apps also lacked access to platform APIs, such as address books, cameras, and Bluetoothāfeatures leveraged by native apps to create viral growth and novel experiences. But the absence of smooth UI transitions certainly didnāt help.</p>
<p>The web survived, but the struggle to match native apps remains. A significant response to this challenge was the Single Page App (SPA) architecture.</p>
<h2 id="single-page-apps-a-solution-with-costs">Single Page Apps: A Solution with Costs</h2>
<p>SPAs sidestepped the need to download each state as a separate page and the associated clunky transitions by running the entire application in a single page. Now, transitions from one state to the next could be accomplished with CSS transitions and animations. However, SPAs carried their own set of challenges that we still grapple with today.</p>
<p>First, an entire app essentially needed to be downloaded before it could be used. This was acceptable for apps from an app store, but it conflicted with the webās āinstant-onā philosophy. Long load times were particularly detrimental on mobile. This not only presented a UX challenge but also a cost oneāmobile data was far from cheap a decade ago, and for many people, it still is expensive. Plus, all that app logic had to be served up potentially many times for every user.</p>
<p>The SPA hydration model (downloading an application shell and then rendering the bulk of the app with JavaScript) put significant burdens on device performance. The web's offline and caching capabilities at the time were poor, often requiring the entire app to be downloaded every time it was used. As we learned over time, SPAs also come with SEO challenges, complex codebases, accessibility issues, and maintenance headachesāall stemming, in part, from trying to emulate the native app experience.</p>
<p>It was not entirely in jest that SPAs have been called a "zero-interest rate phenomenon.ā</p>
<h2 id="a-new-hope-view-transitions">A New Hope: View Transitions</h2>
<p>But now, or very soon, we'll be able to create <a href="https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API">multi-page apps with View Transitions</a>. Itās taken well over a decade for the Web platform to respond, but View Transitions, coupled with the experimental <a href="https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API">Speculation Rules API</a>, promise drastically simplified architectures and reduced complexity.</p>
<p>Today, I want to provide a brief overview of whatās coming (and in some cases, already here) and give you the single line of CSS that can add View Transitions to your app or site. As of October 2024, View Transitions are supported in Chrome and Safariās Technology Preview.</p>
<h2 id="introducing-view-transitions">Introducing View Transitions</h2>
<p>The View Transition API enables:</p>
<ul>
<li>Animating between DOM states in SPAs.</li>
<li>Animating the navigation between documents in multi-page apps (MPAs). Or "web sites" as we used to call then.</li>
</ul>
<p>Now View Transitions <em>can</em> get complex, as they enable sophisticated animations, but letās start simply.</p>
<h3 id="a-simple-page-transition">A Simple Page Transition</h3>
<p><video src="https://htmhell.dev/adventcalendar/2024/3/simple-abrupt.mov" controls=""></video></p>
<p>Take this classic page transition ā quick and clean, thanks to the use of CSS's aspect-ratio and the lazy loading of images. But itās abrupt. Now, letās add the magic line of CSS to smooth things out:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token atrule"><span class="token rule">@view-transition</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">navigation</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Alright, I cheated; itās three lines. But you could write it as a single line (CSS isn't Python afterall)!</p>
<p><video src="https://htmhell.dev/adventcalendar/2024/3/simple-smooth.mov" controls=""></video></p>
<p>Here it is in action (as supported in Chrome and Safari Tech Preview). The transition is smoother, less abrupt. No JavaScript, libraries, or dependenciesājust lovely progressive enhancement. If you want to see it live, visit <a href="https://conffab.com/">Conffab</a>, where this effect is used for every page transition.</p>
<p>Thereās no reason not to add this to any website today. And more and more visitors will experience nicer transitions over time, at almost no cost to you as the technology becomes more widely supported.</p>
<h2 id="adding-dynamic-transitions">Adding Dynamic Transitions</h2>
<p>We can also make transitions more dynamic with just a bit more CSS.</p>
<p>Here's a before and after video. First, how browsers traditionally rendered page transitions</p>
<p><video src="https://htmhell.dev/adventcalendar/2024/3/complex-abrupt.mov" controls=""></video></p>
<p>And now how Chrome renders this new transition we'll detail below.</p>
<p><video src="https://htmhell.dev/adventcalendar/2024/3/complex-smoth.mov" controls=""></video></p>
<p>I hope you'll agree it's a much more engaging effect. So, how do we achieve this?</p>
<p>In our main page, where we have links to individual sessions, we have one end of the transition that looks like these:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</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>speaker-marco-rogers<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">view-transition-name</span><span class="token punctuation">:</span> marco-rogers-hero</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</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>speaker-maria-farrell<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">view-transition-name</span><span class="token punctuation">:</span> maria-farrell-hero</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>Then in the target pages, we have elements like these:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</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>session-details<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">view-transition-name</span><span class="token punctuation">:</span> marco-rogers-hero</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</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>session-details<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">view-transition-name</span><span class="token punctuation">:</span> maria-farrell-hero</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>Notice how the two āendsā of the transition have matching <code>view-transition-name</code> values. This tells the browser which DOM elements to transition between.</p>
<p>Inline styles arenāt always ideal, but in this case, it was the easiest solution given the existing codebase. You could alternatively apply the style with CSS, if the endpoints can be uniquely selected on a page (since <code>view-transition-name</code> values must be unique on any given page so the browser knows which elements to create a transition from and to).</p>
<p>Currently, this more complex example may have issues in Safari 18's Tech previews, but Iām confident theyāll be resolved soon. Above all, itās great to see Safari investing in this feature!</p>
<h2 id="whats-next?">What's Next?</h2>
<p>First, add the simple one-liner to all your sites for smoother page transitions.</p>
<p>Then, explore adding more specific transitions like in the second example. For generated pages, adding <code>view-transition-name</code> attributes may not present much challenge, and depending on your HTML structure, you could add them with just a bit of CSS.</p>
<p>Now, animation effects can cause difficulties for some users, such as those with vestibular disorders, so we should take our user's preferences about motion into account. Doing that for View Transitions is very straightforward. We add</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-reduced-motion</span><span class="token punctuation">:</span> reduce<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token atrule"><span class="token rule">@view-transition</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">navigation</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Add this after our original <code>@view-transition</code> rule. Now, if the user's preferences are set to reduced motion, no view transitions will be triggered on page navigation.</p>
<!-- MM: or instead only apply it when the user has no preference
@media (prefers-reduced-motion: no-preference) {
@view-transition {
navigation: auto;
}
} -->
<!--
KS: I came here to say the same thing. The advantage of Manuel's
approach are numerous, including that you're building the special
effects inside of a query, instead of using a query to push back
against things that can end up scattered around in a stylesheet.
-->
<!-- JA: I guess the challenge is most people then won't get any effects since most folks won't have set a motion preference? -->
<!-- MM: No, because no-preference is the default-->
<p>Finally, take a deeper look at what else the View Transition API can doāthis is just the beginning.</p>
Starting off right: Where autofocus shines
2024-12-02T00:00:00Z
https://htmhell.dev/adventcalendar/2024/2/
by Kilian Valkhof<br><p>Focus is where the user is on your website. It's what makes it possible to navigate your site with the keyboard or other assistive technologies, and it's how a browser knows which form element you're typing in. It's vital to get right if you want to build good websites.</p>
<p>Whenever you're dealing with code that can āstealā focus you have to be aware of how that affects your visitor. So it's not strange that many folks will tell you to leave focus alone, to stay away from <code>tabindex</code> and <code>autofocus</code>.</p>
<p>All of that advice is good advice: you <em>should</em> be careful when moving the focus away from where it would normally be and where the user would expect it to be, or you'll quickly find yourself in a world of accessibility issues.</p>
<p>But if you start off on the right foot, managing focus is a delightful HTML detail that makes your site a joy to use.</p>
<h2 id="autofocus">Autofocus</h2>
<p>The <code>autofocus</code> attribute can be added to any element to make it <strong>auto</strong>matically <strong>focus</strong>ed on page load, skipping over any other elements that might have been focused before. Any element that can be focusable can also have <code>autofocus</code> applied to it, like buttons, links, and form elements.</p>
<p>As mentioned above, you'll rarely want to use <code>autofocus</code>. Messing with people's focus usually doesn't make them want to use your website more. It will probably make them want to use it less, so don't autofocus your purchase button or search field.</p>
<p>But there's a place where <code>autofocus</code> shines: On single-purpose pages containing forms.</p>
<p>...which is a bit of an obtuse way to say ālogin pagesā, āsignup pagesā, āpassword reset pagesā and āTwo-factor authentication (2FA) pagesā, you get the idea.</p>
<h2 id="adding-autofocus">Adding Autofocus</h2>
<p>Your login page has one purpose: to get the user to log in.</p>
<p>The user has one goal: to log in.</p>
<p>So why not make it as easy as possible for them?</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>username<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">autofocus</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>password<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<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>password<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>Add autofocus to the first field and your visitor can start typing as soon as the page loads. No need to click or tab or search for forms on the page.</p>
<p>If the user uses assistive technology, it will announce the label and the field, and then announce that the field is focused.</p>
<p>Similarly, your password reset page (single field) and signup page (single purpose) will have the same structure and will likewise benefit from having the first field focused.</p>
<p>If your login page is followed by a 2fa page, then that <code>autofocus</code> is <del aria-hidden="true">even more useful</del> <ins>vital</ins>.</p>
<p>Few things are as "throw pc out of the window"-frustrating as frantically typing in that 2fa code as the last few seconds tick away, only to find the field wasn't focused. And now you have to do it all over again.</p>
<p>Argh!</p>
<p>Some things just get to me.</p>
<h3 id="when-not-to-use-autofocus-on-single-purpose-pages">When not to use autofocus on single-purpose pages</h3>
<p>Even if you have a page with a single purpose, you might not want to add autofocus.</p>
<p>If you have a login form but people can also use social logins (Google, GitHub, and the like) and you don't know which one they'll use don't add an autofocus.</p>
<p>Instead, keep track of what they use to log in. As soon as you know which one they're using you can store that in a cookie (or localStorage) and focus on the field or button they need to interact with the next time they visit.</p>
<h2 id="conclusion">Conclusion</h2>
<p>So let's get back to the point: <code>autofocus</code> is a great way to make your forms more user-friendly, as long as you use it in the right place.</p>
<p>Are you building a page that has:</p>
<ul>
<li>A single purpose</li>
<li>A single form</li>
<li>No other choices available</li>
</ul>
<p>Then <code>autofocus</code> is your and your visitor's friend.</p>
<p>So go ahead and use it on your login pages, your signup pages, your password reset pages, and your 2fa pages. Your users will thank you for it.</p>
A link on a logo in the header, what should the alt-text be?
2024-12-01T00:00:00Z
https://htmhell.dev/adventcalendar/2024/1/
by Rian Rietveld<br><p>It's a common pattern to use a logo in the header as a link to the homepage.<br />
But what should be the alt-text of that image?<br />
"the site name" or "homepage" or "logo" or "IMG_123"? Does it even matter?</p>
<p>Fun fact: the alt text of the image inside a link, will be added to the link text.</p>
<p>The problem with linking a logo is that it serves 2 purposes:</p>
<ul>
<li>a logo, that tells you which site you are visiting;</li>
<li>a link, that leads to the homepage.</li>
</ul>
<p>So add both that info in the alt text. Explain what's on the image and where the link leads to: "Site name logo, to the homepage".</p>
<p><strong>Note</strong>: Start the alt text with the visible text, then the link will be easier to target for people using voice recognition software.</p>
<p>Let's take as example the logo of my home town Leidschendam-Voorburg.</p>
<p>In code (simplified):</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <br /><span class="highlight-line"> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Leidschendam-Voorburg logo, to the homepage<span class="token punctuation">"</span></span> </span><br /><span class="highlight-line"> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>logo-lv.svg<span class="token punctuation">"</span></span> </span><br /> <span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>Generated HTML:</p>
<a href="https://www.lv.nl/en">
<img alt="Leidschendam-Voorburg logo, to the homepage" src="https://htmhell.dev/adventcalendar/2024/1/logo-lv.svg" width="300" height="76" loading="lazy" style="background-color: #1a194e; padding: 1em;" />
</a>
<p>VoiceOver in Safari will announce this as:<br />
"Link, image, Leidschendam-Voorburg logo, to the homepage".<br />
<img alt="Screenshot of this VoiceOver output." src="https://htmhell.dev/adventcalendar/2024/1/voice-over.png" width="432" height="133" loading="lazy" /></p>
<p>All info is there.</p>
<p>There is an advantage of using the alt text instead of an aria-label solution. When the connection is slow, the alt text will show up before the image does and already informs all users of the site name and link destination. It's quite robust.</p>
<h2 id="discussion">Discussion</h2>
<h3 id="do-i-need-the-word-logo?-isnt-that-redundant-like-the-word-image-or-picture?">Do I need the word logo? Isn't that redundant, like the word image or picture?</h3>
<p>Yes, in this case the fact that this is a logo tells important info.</p>
<p>Not only for blind users, but for all screenreader users. If you are visually impaired but not totally blind, you may see the image vaguely. With the addition of the word logo, you also know what the image is.</p>
<p>The site name is required, but the logo gives extra context.</p>
<h3 id="isnt-this-a-violation-of-253-label-in-name?">Isn't this a violation of <a href="https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1&showtechniques=253#label-in-name">2.5.3 Label in Name</a>?</h3>
<p>No, the visible name is in the alt text, which becomes the accessible name of the link. Adding extra context to the link is common practice.</p>
<p>Compare this with the notorious "Read more" link in a card. A common pattern is to use a sr-only CSS class (or equivalent) with additional text:<br />
<code><a href="url">Read more<span class="sr-only"> about cute kitten</span></a></code> .</p>
<ul>
<li>You see Read more.</li>
<li>You hear Read more about cute kitten.</li>
</ul>
<p>In our case:</p>
<ul>
<li>You see the site name and logo; the visual position tells the link destination.</li>
<li>You hear the site name, the fact it's a logo and the link destination.</li>
</ul>
<p>The whole visible text should be in the accessible name, but extra information, like the meaning of the image and link destination can be added for context. That way the <a href="https://www.w3.org/WAI/WCAG22/quickref/#link-purpose-in-context">purpose of the link</a> is explained.</p>
<h3 id="why-didnt-you-use-an-aria-label?">Why didn't you use an aria-label?</h3>
<p>Of course there are other and also valid solutions. By using plain simple HTML I want to honor the <a href="https://www.w3.org/TR/using-aria/#rule1">first rule of ARIA</a>:</p>
<blockquote>
<p>If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.</p>
</blockquote>
<p>Happy Holidays!</p>
Never underestimate HTML
2023-12-24T00:00:00Z
https://htmhell.dev/adventcalendar/2023/24/
by Lara Aigmüller<br><p>āHTML is easy.ā, āFrontend development is easier than backend development.ā, āUpdating the UI should be a simple task once the backend is ready.āāthese and other similar statements reached my ears time and again during my career as a web developer.</p>
<p>Very often, these made me sad. š„²</p>
<p>The reason is that I spend most of my working hours writing frontend code including HTML, CSS, and JavaScript (JS) (actually, TypeScript most of the time). When somebody tells me, that my job is āeasyā, I feel like my skills are nothing special and another developer could replace me anytimeā¦</p>
<p>What follows now is an article about thoughts that came to my mind in the last couple of years when working together with different people from different teams with different backgrounds in HTML and frontend technologies in general. This is an article about the many āwhysā in my head and possible answersā¦</p>
<h2 id="why-do-people-think-html-is-easy?">Why do people think HTML is easy?</h2>
<p>What is meant by āeasyā anyway? Whether something is easy or not is usually in the eye of the beholder. When I know a technology or programming language well, itās easier for me than somebody new to the field and inexperienced.</p>
<p>Some people tend to make assumptions about the complexity of frontend development and in my experience those people are the ones not working with frontend technologies and especially HTML on a daily basis. Here are a few reasons I can think of why this happens:</p>
<ul>
<li>The HTML syntax is not that hard to learn. Put together some angle brackets, tag names and key-value pairs (a.k.a. attributes) and youāre good to go!</li>
<li>The HTML syntax is machine and <em>human</em> readable, which was one of the ideas behind XML-like languages.</li>
<li>Once youāve written a couple of lines of code in a <code>.html</code> file, no compilation or build steps are required to see the result of your work immediately when you open the same file in a browser.</li>
<li>HTML has a low barrier of entry. In some WYSIWYG editors there is an option to switch to the ācodeā view, and you can fiddle around with the HTML version of your text, no matter if you know what youāre doing or not. (You have a preview available, what could possibly go wrong?)</li>
<li>Browsers are great and forgive a <em>lot</em> of mistakes (J. David Eisenberg described this in a pretty old <a href="https://alistapart.com/article/forgiving/">article about āforgivingā browsers</a>, that contains some thoughts that are still valid today.). When you open a syntactically broken HTML page the chances are high that the browser still renders <em>something</em>. You forgot to close a tag? No problem. You added an unknown tag or attribute? The browser just ignores it. Compared to a programming language where the whole app crashes because of a missing semicolon, yes, this feels āeasyā.</li>
</ul>
<p>Letās go one step further and think about why people tend to compare web technologies or contrast frontend with backend development.</p>
<h2 id="why-do-people-think-frontend-development-is-easy?">Why do people think frontend development is easy?</h2>
<p>Programming the backend of a website or application is the hard part, creating the frontend an easy one. Right? Sometimes, it feels to me that this is the common opinion among many developers.</p>
<p>And again, Iām looking for the answers to the āwhyā and came up with the following thoughts:</p>
<p>Users and project stakeholders are often not confronted with the business logic and backend code at all, but only with the user interface (UI). Adding thoughts about where to put which button or piece of information and how the UI should work in general is easier because itās just more tangible than complex database queries or nested for-loops and if-statements. The backend is a black box that does its magic and spits out data for the frontend. The frontend developer ājustā needs to display this data, add colors, and build a layout (using CSS) and the work is done.</p>
<p>Luckily, there are many frontend component libraries that can be used. Put together some (predefined) views, fill the data in there, and you donāt even need to think about colors and layouts anymore ā isnāt that great? So, with this amount of help, almost anyone can build great frontends without too much HTML/CSS knowledge!</p>
<p><code></sarcasm></code></p>
<h3 id="frontend-built-by-"non-frontend"-developers">Frontend built by ānon-frontendā developers</h3>
<p>I made the experience that people who thought programming the frontend was the easy part of web development (and would never call themselves āfrontend developerā but rather āfull-stack developerā) made the biggest mistakes in their frontend code (even with help of frameworks and libraries).</p>
<p>Heydon addressed this topic in <a href="https://medium.com/@Heydon/reluctant-gatekeeping-the-problem-with-full-stack-e9ad836570f6">āReluctant Gatekeeping: The Problem With Full Stackā</a> where he wrote:</p>
<blockquote>[...] if you put someone in charge of all of these things and more [...] itās highly likely they are going to be much weaker in some areas than others. [...] In my experience, men especially earn kudos for their knowledge of JavaScript or Python, but little from CSS skills. CSS, which makes things look āprettyā, is considered feminine.</blockquote>
<p>Some of the mistakes mentioned above were actual syntax errors, others were problems with semantics, performance, accessibility, etc. In prototype presentations or during testing, often they arenāt noticed because the browser forgives them (see above), performance is usually good on the developerās machine, no accessibility tests are being made⦠The result is: it works, the browser displays whatās expected, everything seems to work fine and can be shipped to production.</p>
<h2 id="why-is-broken-html-code-a-problem?">Why is broken HTML code a problem?</h2>
<p>Once the code is deployed to production, the first problems might come up. Users who were not part of the development process interact with the product for the first time and run into issues, some of which may be caused by broken HTML code, impairing the user experience.</p>
<h3 id="bad-user-experience">Bad user experience</h3>
<p>Letās have a look at problems based on bad HTML code the end-user might stumble upon:</p>
<ul>
<li>frustrating forms (hereās an <a href="https://www.scale.at/blog/forms-ux-problems-solutions">article of mine about bad user experience when using forms on the web</a> with some examples)</li>
<li>performance problems (hereās a talk on YouTube by Harry Roberts about what can go wrong in your HTML documentās āheadā ā <a href="https://www.youtube.com/watch?v=MHyAOZ45vnU">Get your āheadā straight</a>)</li>
<li>wrong usage of headlines (<code>h1</code>-<code>h6</code>) or missing text alternatives for non-text content, which is bad for people using a screen reader</li>
<li>wrong/no usage of interactive elements (āA div is not a button!ā) or a confusing tab order of elements, which is bad for people using the keyboard for interaction</li>
<li>basically everything you can find on <a href="https://www.htmhell.dev/">htmhell.dev</a></li>
<li>broken HTML code which leads to broken JavaScript and thus broken functionality</li>
</ul>
<h3 id="bad-developer-experience">Bad developer experience</h3>
<p>Not only the users of your sites and applications might have a hard time using your product when the HTML/frontend code isnāt correct, also your fellow developers could have their āfacepalm momentsā, becauseā¦</p>
<ul>
<li>ā¦when HTML code is badly structured, itās harder to write good CSS code for it. To put it differently: HTML code often has to be adjusted once CSS comes into play. When you have experience in writing both languages, the chances are high that your HTML <em>and</em> your CSS code turn out great and maintainable.</li>
<li>ā¦when HTML code of your projectās UI components isnāt flexible, you might not be able to reuse them or scale the project at all without required refactoring when a new feature is requested.</li>
<li>ā¦when developers are not working <em>with</em> the platform but try to reinvent the wheel and not taking browsersā capabilities into account (like using JavaScript to navigate from one page to another instead of just using a link), bugs might be more likely and fixing them without breaking another part of the application gets harder.</li>
</ul>
<h2 id="why-should-we-care?">Why should we care?</h2>
<p>As mentioned above, browsers are great and forgive a lot of mistakes; websites might work for many people, even if they are not accessible or performant or even if parts of them break. Maintaining these websites might also be done by good-hearted/ill-fated developers who donāt mind working on a smelly codebase.</p>
<p>But Iām sure we can do better. šŖ</p>
<p>The goal should be that websites (and web applications) work for most, no, for <em>all</em> people browsing the web and we as developers should build products respecting the needs of all our (potential) users. Writing scalable and maintainable code leads not only to accessible, fast, and usable websites but also to happy developers.</p>
<h2 id="its-not-only-about-writing-html">Itās not only about writing HTML</h2>
<p>Iāve met people who started learning web development by diving head-first into the newest frameworks and then built a simple website using Angular without learning the basics first. The problem: without basic HTML (or CSS or JS) knowledge, the most popular framework doesnāt help you succeed; on the contrary. Youāre using a sledgehammer to crack a nut. No matter which tools you use, the code that gets shipped to the browser is HTML, CSS, and JavaScript and knowing how these languages work is required to build great applicationsāno matter which newfangled technology youāre throwing at your problems.</p>
<p>Writing HTML in itself isnāt that hard, no.</p>
<p>But: building user interfaces by elegantly composing this languageās features with CSS, creating pleasant designs and user experiences worth remembering requires experience and skills that should not be underestimated. Neither should HTML; itās one of the languagesāif not the most important one of them allāthat shape the web.</p>
<p>Related thoughts about the value of (expertise in) web frontend development were shared by</p>
<ul>
<li>Christian Heilmann, who <a href="https://www.linkedin.com/pulse/ongoing-defence-frontend-full-time-job-christian-heilmann-/">defended frontend development as a full-time job</a></li>
<li>Jeremy Keith, who is worried that we might have <a href="https://adactio.com/journal/20442">āreached a state where the more complex over-engineered approach is viewed as the defaultā</a></li>
<li>Andy Bell, who describes that <a href="https://andy-bell.co.uk/front-end-is-so-much-more-than-building-designs/">front-end is so much more than building designs</a></li>
</ul>
<p>Letās stop comparing web technologies and their values. Letās not discuss whatās easier/harder to do or more/less important. Letās work in teams, listen to and learn from people with expertise, no matter whether they shine in writing HTML, CSS, TypeScript, PHP, Python, {add-your-favorite-language-here}⦠letās make the web a great place together and value people who work on the front of the frontend more!</p>
<hr />
<p>P.S.: Thanks to <a href="https://mastodon.social/@felixh10r">Felix</a> for always proofreading my articles and expanding my English vocabulary. Thanks to Manuel for maintaining HTMHell and creating this inspiring advent calendar.</p>
The devil is in the details: a look into a disclosure widget markup
2023-12-23T00:00:00Z
https://htmhell.dev/adventcalendar/2023/23/
by Cristian Diaz<br><p><a href="https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/">Disclosure widgets</a> are one of the most common component patterns you can find on the web. It consists of a button that can hide or show information when you click it. It's also one of the straightforward components to make from a technical standpoint.</p>
<p>Just a quick note: this article will focus on the most basic form of it to show or hide content. A navigation menu can be considered as a disclosure widget but it has other features that wonāt be the main focus of this article.</p>
<p>This component pattern has two approaches: the first one is creating a <code><button></code> element with the attribute <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-expanded">aria-expanded</a> that will toggle between <code>true</code> and <code>false</code> when you want the content to be shown or hidden. Additionally, you want to create a relationship between those two elements with <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-controls">aria-controls</a> to indicate the button controls the presence of this element, like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-expanded</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 attr-name">aria-controls</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>accordion<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>What are cephalopods?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>accordion<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>A cephalopod is any member of the molluscan class <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>Cephalopoda<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> such as a squid, octopus, cuttlefish, or nautilus.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>This approach requires JavaScript to modify the <code>aria-expanded</code> attribute. However, we can pair it with a pure CSSā <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Next-sibling_combinator">n</a><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Next-sibling_combinator">ext-sibling combinator</a> to control showing and hiding the disclosureās content.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">button[aria-expanded="false"] + div</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none</span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">const</span> button <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"button"</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">button<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">element</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> isExpanded <span class="token operator">=</span> element<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>isExpanded <span class="token operator">===</span> <span class="token string">"true"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> element<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">,</span> <span class="token string">"false"</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> element<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">,</span> <span class="token string">"true"</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span></span></code></pre>
<p>Creating a disclosure widget that requires JavaScript also means you should work on a progressively enhanced version of it in case JavaScript doesn't load. There are multiple approaches to this, a very common one is using heading instead of buttons, and when JavaScript loads, replacing the header with a button:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>What are cephalopods<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>A cephalopod is any member of the molluscan class <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>Cephalopoda<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> such as a squid, octopus, cuttlefish, or nautilus.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>All of this sounds like quite some work for a relatively simple component pattern, it'd be cool if we had a native option to make a disclosure widget... Oh, wait, we have one! This is where our second approach enters, and that's using the <code><details></code> and <code><summary></code> elements. Let's come back to our initial markup and let's modify it with this approach.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>What are cephalopods?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>A cephalopod is any member of the molluscan class <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>Cephalopoda<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> such as a squid, octopus, cuttlefish, or nautilus.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<p>And that's it! This disclosure widget works, and it's accessible, so I guess the answer would be using this one instead of using the first approach, right? Well, as usual, the reality is more nuanced than that. As much as I'd prefer to use native options for component patterns, the answer to that question is a definitive āIt dependsā.</p>
<p>When picking a disclosure widget markup, the author needs to balance the strengths and limitations of each approach, and consider what content it will contain. This article will talk about those nuances you should keep in mind about choosing one markup approach or another, mostly focusing on screen reader accessibility because both of them meet the requirements of keyboard navigation with no problem at all.</p>
<h2 id="experience-across-different-browsers">Experience across different browsers</h2>
<p>The first factor to keep in mind is consistency. When you use a progressively enhanced button approach, you know that it'll be the same for screen readers. Both markup approaches will read them as a button and will read the <code>aria-expanded</code> attribute similarly. But with <code><details></code> and <code><summary></code> things change quite a bit when you start taking into consideration different screen readers and browsers.</p>
<p>When you use <code><details></code> and <code><summary></code>, NVDA and JAWS both narrate it as a button in Chrome, and they'll mention if the element is collapsed or expanded. But when you use the same markup in Firefox, they narrate the marker (that is, the visual indicator is being used to indicate the element is collapsed or expanded) as well. My previous example will be read as āFilled right pointing small triangle, what are cephalopods, button, collapsedā. And when we start talking about mobile, the experiences vary a lot.</p>
<p>This is not bad per se, but in some browsers, mentioning the marker can feel a bit like noise, but buttons on the other hand offer a cleaner more consistent experience.</p>
<p>The key point in this section is that you should ask yourself this question: am I okay with letting the screen reader experience be different between different screen readers and browsers? If <strong>you're all right with this</strong>, use <code><details></code> and <code><summary></code>, but if <strong>you'd prefer a more consistent experience all around</strong>, use a progressively enhanced <code><button></code>.</p>
<h2 id="wait-i-can-modify-this-default-marker-right?">Wait, I can modify this default marker, right?</h2>
<p>By default <code><details></code> and <code><summary</code> have this triangle arrow that will change when it is expanded or collapsed. Maybe you have a design that has a different indicator, can you modify that? The answer is yes, but there is a <em>huge</em> caveat here:</p>
<p>As Manuel mentions in his article <a href="https://www.matuzo.at/blog/2023/details-summary">details/summary inconsistencies</a>, you can modify the default arrow by using those CSS rules:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">summary</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">list-style</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>This will hide the arrow in all browsers except Safari, so youāll need an additional CSS rule:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">summary::-webkit-details-marker</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>This deletes the indicator in all browsers, but this brings a big problem: as Manuel's tests in the same article show, some combination of screen readers and browsers will use the arrow indicator as part of the accessible name, and if you remove them, it'll stop indicating its state once the user press the <code><summary></code> element.</p>
<p>This behavior is particularly notorious with VoiceOver and Firefox because when you remove the marker, it'll indicate nothing when the user expands the content.</p>
<p>For this reason, my suggestion here is that if the design <strong>has a different indicator for the expanded/collapsed states than the default option,</strong> you should use a progressively enhanced <code><button></code> approach. On the other hand, if <strong>you are ok with the default marker</strong>, you can stick with <code><details></code> and <code><summary></code>.</p>
<h2 id="browsers-in-page-search">Browser's in-page search</h2>
<p>Those previous points are making look <code><details></code> and <code><summary></code> as an unreliable option, but there is something that those elements add to the table that a progressively enhanced button option can't.</p>
<p>As Manuel mentions in his article <a href="https://www.matuzo.at/blog/2023/details-find-in-page">the details element and in-page search</a>, Chromium-based browser in-page search option can look for content even <strong>when the <code><summary></code> element is collapsed</strong>. This is an important factor to keep in mind because this improves usability for someone who uses the in-page search.</p>
<p>As a side note, do you remember when I said I would not focus on disclosure widget variants like a navigation menu? Well, between the different ways a screen reader displays this component and now taking into consideration the in-page search option, makes <code><details></code> and <code><summary></code> <strong>unsuitable for a navigation menu</strong>.</p>
<p>So, the key point of this section: if you think <strong>letting the user find a keyword using the browser in-page search is important</strong>, stick with <code><details></code> and <code><summary></code>, otherwise, use a progressively enhanced button.</p>
<h2 id="do-i-need-to-always-hide-the-content?">Do I need to always hide the content?</h2>
<p>The main selling point of <code><details></code> and <code><summary></code> is that they'll hide the disclosure widgetās content always, even where there is no JavaScript involved. This is something really important in some scenarios.</p>
<p>The first example that comes into my mind is a <a href="https://www.sistersincrime.org/page/contentwarnings">content warning</a> if it's visible, can cause legitimately bad experiences or trigger a PTSD (Post traumatic stress disorder) episode for a person.</p>
<p>You <em>could</em> use a progressively enhanced button, but if JavaScript doesn't load, there is a risk, and depending on the context, it can be <strong>critical</strong> to hide this content even in those scenarios. Kitty Giraudel created a content warning component using only <code><details></code> and <code><summary></code> in her article, <a href="https://kittygiraudel.com/2022/09/04/a-content-warning-component/">A content warning component</a>.</p>
<p>My key point here is that you need to consider <strong>how critical is to hide the content as an initial state,</strong> no matter what. If it's necessary, consider using <code><details></code> and <code><summary></code>, otherwise, using a progressive enhanced button is a good option.</p>
<h2 id="use-of-headings">Use of headings</h2>
<p>There is quite an interesting thing you can do with a disclosure widget made with the progressively enhanced <code><button></code>, and that's adding headings on them. This makes it easier for screen reader users to navigate to this part of the content. The process changes a bit depending on what approach you want to use. For this approach, the markup would look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-expanded</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 attr-name">aria-controls</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>accordion<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> What are cephalopods?</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>accordion<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>A cephalopod is any member of the molluscan class <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>Cephalopoda<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> such as a squid, octopus, cuttlefish, or nautilus.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>There is a catch though: the <code><button></code> is now inside the heading, so itāll be undetected by the next-sibling combinator. There are two options: we can use to solve this issue:</p>
<p><strong>Modifying our script:</strong> We can make a slight modification in our script and making the adjacent container have the <code>hidden</code> attribute .Then, we toggle it when the <code><button></code> has the proper <code>aria-expanded</code> value, like this:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">const</span> button <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"button"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">const</span> accordionElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"[data-content]"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line">button<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">element</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> isExpanded <span class="token operator">=</span> element<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>isExpanded <span class="token operator">===</span> <span class="token string">"true"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> accordionElement<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"hidden"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> element<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">,</span> <span class="token string">"false"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> accordionElement<span class="token punctuation">.</span><span class="token function">removeAttribute</span><span class="token punctuation">(</span><span class="token string">"hidden"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> element<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">,</span> <span class="token string">"true"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p><code>:has()</code>: The other option is using the (relatively) new CSS <code>:has()</code> selector like this:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">h3:has([aria-expanded="false"]) + div</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The problem with <code>:has()</code> <a href="https://caniuse.com/css-has">is its browser support</a>. At the moment of writing this article, Firefox just shipped it in the latest version (121), so browser support may make use of this selector not robust enough to be reliable (for now!).</p>
<p>Can you do that with <code><details></code> and <code><summary></code>? Yes, do I recommend it to do it? No, it has some inconsistent and even buggy behaviors when you use a screen reader to activate them.</p>
<p>Let's start with checking how it is āpossibleā, first, we add our heading inside <code><summary></code> like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>What are cephalopods?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>A cephalopod is any member of the molluscan class <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>Cephalopoda<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> such as a squid, octopus, cuttlefish, or nautilus.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<p>Keep in mind you need to add the <code><summary></code> element as the direct child of <code><details></code>, otherwise, itāll not be recognized as our disclosureās trigger. Now, There is one detail, this is how it'd look:</p>
<figure class="u-mb">
<details>
<summary><h3>What are cephalopods?</h3></summary>
<p>A cephalopod is any member of the molluscan class <i>Cephalopoda</i> such as a squid, octopus, cuttlefish, or nautilus.</p>
</details>
<figcaption>The title āWhat are cephalopods?ā is just below the arrow marker when it should be at its side</figcaption>
</figure>
<p>Then, we'd need to use CSS to put the heading at the same level, but if you do it with, for example, <code>display: flex</code>, it'll not show the marker, and as we saw before, this will affect screen reader experience for some combinations of screen reader and browser. My approach here was making the heading inline, and that worked well, like this:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">summary h3</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> inline<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Why I don't recommend doing that with <code><details></code> and <code><summary></code>? With both approaches, you can use screen reader shortcuts to navigate directly to a heading, but there is a caveat here:</p>
<p>With the <code><button></code> approach there will be no problem: the button is inside the heading and if the screen reader's <a href="https://support.freedomscientific.com/teachers/lessons/4.2.3_VirtualPCCursor.htm">virtual cursor</a> is over the heading, you can control the button by pressing the <code>Enter</code> or <code>Space</code> keys. This is useful when you use screen readerās shortcuts to navigate to a heading (like using the H key).</p>
<p>There catch here comes with <code><details></code> and <code><summary></code> because it has way less consistent results. To prove that, I decided to test each disclosure with heading markup next to each other. You can take a look at the example I used right here:</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="wvRNJye" data-user="ItsCrisDiaz" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/ItsCrisDiaz/pen/wvRNJye">
Untitled</a> by Cristian Diaz (<a href="https://codepen.io/ItsCrisDiaz">@ItsCrisDiaz</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p><a href="https://codepen.io/ItsCrisDiaz/pen/wvRNJye">CodePen</a></p>
<p>This test used the next combinations of screen readers and browser:</p>
<ul>
<li>NVDA and JAWS with both Chrome and Firefox.</li>
<li>Narrator with Edge</li>
<li>VoiceOver with Safari, Chrome and Firefox</li>
</ul>
<p>And those are the results:</p>
<ul>
<li>NVDA works perfectly with both browsers, you can navigate using the key shortcuts and you can activate them with <code>Enter</code> or <code>Space</code>.</li>
<li>With JAWS you <em>canāt</em> navigate to a heading inside the <code><summary></code> element, making this feature not advisable.</li>
<li>Narrator with Edge is surprisingly buggy, it read both headings at the same time as if they were just one, and it didnāt let me to activate any of them. However, once I removed the <code><details></code> element it worked normally, so itās safe to say adding a heading inside <code><summary></code> is not advisable.</li>
<li>With VoiceOver, you can navigate to a heading using the key shortcuts, <em>however</em>, you canāt activate the <code><summary></code> element that way. You can activate it navigating with the <code>Tab</code> key but for some reason, if VoiceOver visual cursor is on the heading itself, it canāt be activated. This can create confusion for screen reader users because they found a heading, but they will not find extra content below it because it's a disclosure widget.</li>
</ul>
<p>So, key point of this section: if you think <strong>adding heading navigation to your disclosure widget improves the user experience for screen reader users</strong>, you'd be better using a <code><button></code> element, otherwise, use <code><details></code> and <code><summary></code>.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Having a native way to make a disclosure widget is great! But as usual, things are not that easy because browsers' implementation tend to vary in details that you need to take into consideration. It's not the first HTML element that has inconsistencies among browser (As an example, just look at Manuel's article <a href="https://www.matuzo.at/blog/2023/focus-dialog">O dialog focus, where art thou?</a> to check how browsers implement keyboard focus with the <code><dialog></code> element) and it certainly won't be the last.</p>
<p>In my mind, there is no doubt that browserās implementation of <code><details></code> and <code><summary></code>will be more consistent in the future and this article might end up being a thing of the past. But for now, there are important considerations you need to keep in mind before deciding to use one markup approach or another.</p>
ARIA Live Regions
2023-12-22T00:00:00Z
https://htmhell.dev/adventcalendar/2023/22/
by Andrea de Souza<br><p><a href="https://www.w3.org/TR/wai-aria/">ARIA</a> stands for Accessible Rich Internet Applications. It is a set of roles and attributes that makes web page elements accessible to users who require assistive technology, like screen readers, when native HTML alone is not enough. One of these sets of roles and attributes is aimed at defining live regions.</p>
<p>Live regions are areas that, when dynamically updated, trigger screen readers to announce their new content. They are commonly used for notifications that do not receive focus when the new content is added to the page. For example: form errors, loading spinners, search results that appear on dynamic search, and toasts.</p>
<p>Here is a practical example. On an e-commerce website, the notification āproduct added to cartā displays momentarily whenever the āadd to cartā button is pressed. The new content is added to the page, but doesn't receive focus. While sighted users would be able to see it, screen-reader users would not know that somewhere on the page an updated happened. Adding a live region to the parent element that displays this type of notification allows it to be announced by screen readers.</p>
<h2 id="creating-live-regions">Creating Live Regions</h2>
<p>There are two ways widely used to implement a live region in an HTML element: adding the <code>aria-live</code> attribute or adding a role that has an implicit live region.</p>
<p>A third option for specific scenarios is to use the native HTML <code><output></code> element, which has the implicit <code>role="status"</code> property. However, there are accessibility issues associated with it, as described in detail by <a href="https://www.scottohara.me/blog/2019/07/10/the-output-element.html">Scott OāHara</a>.</p>
<h3 id="aria-live-attribute"><code>aria-live</code> Attribute</h3>
<p>The <code>aria-live</code> attribute on an HTML element creates the behaviour of a live region by defining a "politeness setting", which indicates when dynamic updates should be announced to users of assistive technology. This attribute needs to be declared using one of three possible values:</p>
<ul>
<li><code>off</code>: This is the default value. No changes will be announced, unless if the user is focused on the region where the update happens.</li>
<li><code>polite</code>: The screen reader will announce the changes when it has a chance. For example, when the user is idle or once the task being performed at the moment of the notification ends. This option should be used for low priority updates that donāt require immediate attention from the user.</li>
<li><code>assertive</code>: The screen reader will interrupt whatever it is reading to announce the content update. This option should only be used for time-sensitive, critical information.</li>
</ul>
<p>Going back to the e-commerce website example, the āproduct added to cartā message may have an <code>aria-live="polite"</code> attribute.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Product added to cart.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>However, if the user is on a news website and there's a weather alert or an emergency broadcast, then this notification has a high degree of priority and should have an <code>aria-live="assertive"</code>.</p>
<p>Please note that those are generic examples. The <code>aria-live</code> attribute value will depend on different factors. A good way to determine the politeness setting required is to ask directly the users that will be the recipients of the message. One important aspect to keep in mind is that the <code>assertive</code> level is disruptive for screen-reader users and should only be used when absolutely required.</p>
<p>There are other ARIA attributes that, in theory, should provide greater control of how ARIA live regions should behave. But, in practice, they have only partial support and/or their behaviour varies between different screen reader/browser combinations.</p>
<ul>
<li><code>aria-busy</code>: When set to <code>true</code>, it indicates that the element is not yet ready to be announced because itās still being modified. It also means that screen readers <em>MAY</em> want to wait before announcing the changes (<a href="https://www.w3.org/TR/wai-aria-1.2/#aria-busy">W3C</a>). The partial support for this attribute happens when it's set to <code>true</code>, according to <a href="https://a11ysupport.io/tech/aria/aria-busy_attribute">a11ysupport.io</a>. The default value is <code>false</code>. An example that may apply this attribute is to set <code>area-busy="true"</code> while a page is still loading and change it to <code>false</code> once all elements are loaded.</li>
<li><code>aria-relevant</code>: Specifies the updates that are relevant to be announced, which can be either: the addition of new nodes (<code>aria-relevant="additions"</code>); text content, text alternative, or node removal (<code>aria-relevant="removals"</code>); or the addition of text content or text alternative (<code>aria-relevant="text"</code>). The shorthand <code>all</code> includes the three values. Multiple space-separated values can also be used. For example, <code>additions text</code>, which is the default value. Please note that the values <code>removals</code> and <code>all</code> should be used carefully, since only in specific scenarios it's relevant to announce the removal of content. One example would be when a user leaves a chat room. According to <a href="https://www.w3.org/TR/wai-aria-1.2/#aria-relevant">W3C</a>, <code>aria-relevant</code> is an optional attribute and represents a <em>suggestion</em> to assitive technologies. The partial support for this attribute is documented by <a href="https://a11ysupport.io/tech/aria/aria-relevant_attribute">a11ysupport.io</a>.</li>
<li><code>aria-atomic</code>: Specifies if the whole live region should be announced (<code>aria-atomic="true"</code>) or only the content that has changed (<code>aria-atomic="false"</code>) based on the value of <code>aria-relevant</code>. For example, with default value <code>false</code> and the default <code>aria-relevant="additions text"</code>, only the updated nodes and the addition of text content or text alternative will be announced. When set to <code>true</code>, the entire live region will be announced. In this case, screen readers <em>MAY</em> choose to combine multiple changes and present the whole region at once (<a href="https://www.w3.org/TR/wai-aria-1.2/#aria-atomic">WAI ARIA - aria-atomic</a>). Depending on the application, this attribute may be important, but make sure to test the behaviour with different browser/screen reader combinations. Test results for this attribute can be found at <a href="https://a11ysupport.io/tech/aria/aria-atomic_attribute">a11ysupport.io</a> and at <a href="https://pauljadam.com/demos/aria-atomic-relevant.html">Paul J. Adam's website</a>.</li>
</ul>
<h3 id="live-region-roles">Live Region Roles</h3>
<p><a href="https://www.w3.org/TR/wai-aria-1.2/#live_region_roles">The ARIA specification</a> lists five live region roles, according to the type of notification that they announce:</p>
<ul>
<li><code>alert</code>: Time-sensitive notifications that require immediate attention, like form errors.</li>
<li><code>status</code>: Notifications that are not as important as an <code>alert</code>, nor time-sensitive.</li>
<li><code>log</code>: The order in which the notifications are added have meaning, as in a log, and old information may disappear.</li>
<li><code>marquee</code>: The information is non-essential, changes often, and the order is not meaningful.</li>
<li><code>timer</code>: The notification is the amount of elapsed time from a starting point or the remaining time until an end point of a numerical counter.</li>
</ul>
<p>Each live region role has an implicit <code>aria-live</code> attribute:</p>
<ul>
<li><code>role="alert"</code> has <code>aria-live="assertive"</code></li>
<li><code>role="status"</code> has <code>aria-live="polite"</code></li>
<li><code>role="log"</code> has <code>aria-live="polite"</code></li>
<li><code>role="marquee"</code> has <code>aria-live="off"</code></li>
<li><code>role="timer"</code> has <code>aria-live="off"</code></li>
</ul>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions#roles_with_implicit_live_region_attributes">MDN</a> recommends adding a redundant <code>aria-live</code> attribute to the alert, status, and log roles to maximize compatibility.</p>
<p class="highlight">Please keep in mind that <code>role="status"</code> and <code>role="alert"</code> are the only two live region roles that have good browser/screen reader support.</p>
<h3 id="aria-live-attribute-vs-live-region-role"><code>aria-live</code> Attribute vs Live Region Role</h3>
<p>So, what is the difference between using <code>aria-live</code> and a live region role? <code>aria-live</code> is an attribute that creates the behaviour of a live region by defining the āpolitenessā level of how the notification should be announced. A live region role not only creates the behaviour of a live region, but also provides semantic meaning to screen readers. It has an implicit <code>aria-live</code> property.</p>
<p>For example, an element with <code>role="alert"</code> has an implicit <code>aria-live="assertive"</code> property and should inform the screen reader that the notification in that area is really an alert. While the <code>aria-live="assertive"</code> attribute by itself will inform the screen reader that it should interrupt whatever it is reading to announce that notification.</p>
<p>Another difference is that live region roles allow to define an accessible name, which may be important when having more than one live region in the same page. For example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>status<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[id-of-an-element]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>While the definition with <code>aria-live</code> wouldn't allow the addition of an accessible name:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>One last difference specific to live regions defined with <code>role="alert"</code> or <code>role="status"</code> is the implicit <code>aria-atomic="true"</code> property.</p>
<h2 id=""robust"-live-regions">āRobustā live regions</h2>
<p>Unfortunately, live regions donāt behave consistently in all browser/screen reader combinations, even if all roles and attributes are added according to the specification. However, there are a few recommendations that can be followed to make the implementation as robust as possible:</p>
<h3 id="first-insert-the-live-region">First: Insert the live region</h3>
<p>The element that contains the aria-live attribute or live region role should be in the DOM, empty, before the update happens. Ideally, on page load or as close as possible to it. This is important because live regions need to be in the <a href="https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree">accessibility tree</a> in order to be announced. The accessibility tree is a subset of the DOM tree. It provides information from the DOM in a way that can be understood by screen readers and other assistive technologies.</p>
<p>For example, the <code>div</code> below can be part of the initial HTML markup and, later on, used for a dynamic notification:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>status<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Having an element with a live region declared, as the example above, will not trigger any announcement by screen readers. It's only when an update in the content inside the live region happens that the it will trigger screen readers to announce the new content.</p>
<p>If the element with the live region role is dynamically injected in the DOM at the same time as the notification happens, it may not be correctly announced. For example, if an element with a live region is in the HTML, but has the CSS property <code>display: none</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>status<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span>none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Using the CSS properties <code>display: none</code> or <code>visibility: hidden</code> hides the content from users, but also removes it from the accessibility tree. And if the live region is not in the accessibility tree, then it cannot be tracked by screen readers. Although these properties can be dynamically changed with JavaScript to <code>display: block</code> or <code>visibility: visible</code>, it should not be done at the same time as the content update.</p>
<h3 id="second-add-dynamic-content">Second: Add dynamic content</h3>
<p>The dynamic content should be added to descendants of the element that has the <code>aria-live</code> attribute or live role region. Adding to the previous example, the dynamic content can be added as:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>status<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Product added to cart.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="when-not-to-use-a-live-region">When not to use a live region</h2>
<p>Although a live region may seem like the ideal solution to announce a dynamic content update, it is not always the best approach. Here are a few examples when not to use it:</p>
<ul>
<li>If there is an option to replace the live region with static instructions, then do it. It will probably be a better experience for screen-reader users to read additional content than to be interrupted with a notification. For example, a <code><select></code> element updates another area of the page according to the option value. Instead of using a live region for the area that will be updated, it's possible to have static text saying that the content will be updated according to the option selected.</li>
<li>Focus change and live regions firing at the same time are prone to conflicts. If there is a focus change to the element being updated, then a live region is not needed. Going back to the "add to cart" example, instead of having the "product added to cart" message, we can have a "cart flyout" (modal dialog) opening on the side of the screen. Focus may go the modal close button, an interactive element. In this example, there's no need for a live region, but the user should be informed of the new context.</li>
<li>Donāt use live regions to announce state changes that can be communicated through an ARIA state. For example, if using an accordion, use the attribute <code>aria-expanded</code> to indicate if itās opened or closed. If using a toggle button, use <code>aria-pressed</code>.</li>
<li>Donāt use live regions for visual UIs that change constantly. <a href="https://www.youtube.com/live/W5YAaLLBKhQ?si=SxKUbgEKmGxItV8D">Sarah Higley</a> suggests to provide a separate interface in some scenarios. This will keep the live region concise by announcing only relevant information. A practical example would be a search box in which the results update every time the user types a character. Wrapping the element in an ARIA live region would be too verbose because every single change would be announced.</li>
</ul>
<h2 id="tips-to-improve-the-user-experience">Tips to improve the user experience</h2>
<p>There are a few tips that can improve the user experience:</p>
<ul>
<li>
<p>Live region notifications should be concise. Remember that screen-reader users have no control over it. And since the notification is only announced once, thereās no possibility to go back and review it, according to <a href="https://www.youtube.com/live/W5YAaLLBKhQ?si=SxKUbgEKmGxItV8D">Sarah Higley - The Many Lives of a Notification</a>.</p>
</li>
<li>
<p>Notifications should be easy to understand. Screen readers will read everything as plain text. Even if the text has headings, bold content, or any interative content (links or buttons, for example), everything will be read as one block of text. Try reading out loud your notifications and ask yourself if they make sense.</p>
</li>
<li>
<p>Donāt use special characters (anything other than letters and numbers). For example, always use "and" instead of ampersand (&). Use correct grammar. For punctuation, use only commas and periods.</p>
</li>
<li>
<p>If the live region contains a notification specific for screen-reader users that does not appear visually, it is recommended to remove it shortly after it shows up. This is important to avoid screen readers announcing old updates that are not relevant anymore. According to <a href="https://www.youtube.com/live/W5YAaLLBKhQ?si=SxKUbgEKmGxItV8D">Sarah Higley</a>, a notification doesnāt need to stay long in the DOM. Even half a second is enough because the screen reader is watching the live regions for updates, so it will announce any new content, even if itās removed right after. Please note that this recommendation does not apply to notifications that communicate user-facing state and status that other ARIA cannot. It is only for content that can become obsolete.</p>
</li>
<li>
<p>Donāt add too many live regions to the same page. It may cause conflicts if more than one fires at the same time. For example, a "polite" notification may not be announced if an "assertive" notification also happens around the same time. According to <a href="https://www.w3.org/TR/wai-aria-1.2/#aria-live">W3C</a>, the politeness level is an ordering mechanism and "user agents or assistive technologies <em>MAY</em> choose to clear queued changes when an assertive change occurs".</p>
</li>
</ul>
<p>Keep in mind that if a live region can be avoided, take that route. If possible and if it makes sense for your application, move focus and donāt add a live region. However, focus should only move if itās because of a user action. Also, depending on the scenario, adding clear instructions may be enough, instead of using a live region.</p>
<h2 id="taking-control">Taking Control</h2>
<p>Screen readers allow users to control how they navigate a web page. For example, they can go first through all the headings of a page before deciding to read a full section. They can also go back and read a specific content again.</p>
<p>But when an element has a live region, the control is taken away from the screen-reader user. They will be forced to hear the notification, either in a <em>polite</em> or <em>assertive</em> way, and that announcement only happens once.</p>
<p>Therefore, developers should use caution when implementing live regions. They can negatively impact a screen-reader user experience if poorly implemented or if there are too many of them, providing non-stop notifications.</p>
<p>If the element requires a live region, then make sure to test the implementation with screen readers to ensure that it works as intended.</p>
<h2 id="testing">Testing</h2>
<p>It is crucial to test live regions with different browser/screen reader combinations. And, whenever possible, get your page tested by a real screen-reader user.</p>
<p>If you need help to get started with screen readers, Sara Soueidan has an article with detailed instructions on <a href="https://www.sarasoueidan.com/blog/testing-environment-setup/">setting up a screen reader testing environment on your computer</a>. The article also suggests the following <a href="https://www.sarasoueidan.com/blog/testing-environment-setup/#which-browser-and-screen-reader-pairings-should-you-test-on?">pairings</a> for testing based on usage and compatibility:</p>
<ul>
<li>JAWS on Chrome</li>
<li>NVDA on Firefox</li>
<li>Narrator on Edge</li>
<li>VoiceOver on Safari for macOS,</li>
</ul>
<p>You will probably notice that the same implementation will not behave the same way across the board. This is because support for live regions vary between how browsers and screen readers are paired together. Since this can result in a poor experience for some users, it's essential to test thoroughly to uncover potential issues and fix them.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Live regions provide a solution for announcing dynamically added content that would otherwise be missed by screen readers, but their behaviour is not consitent across different browser/screen reader combinations. If adding a live region to your page, follow the specification to implement it in the most robust way possible. Donāt add too many live regions to the same page and only make them <code>assertive</code> if itās absolutely necessary. Remember to test your live regions thoroughly and re-test often to confirm that they still work as intended. Even better, have your page tested by a real screen-reader user.</p>
<h2 id="useful-links-and-references">Useful links and references</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree">MDN Accessibility tree</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions">MDN aria-live regions</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#4._live_region_roles">MDN Live region roles</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output">MDN <output>: The Output element</output></a></li>
<li><a href="https://www.youtube.com/live/W5YAaLLBKhQ?si=SxKUbgEKmGxItV8D">Sarah Higley - The Many Lives of a Notification</a></li>
<li><a href="https://www.scottohara.me/blog/2022/02/05/are-we-live.html">Scott OāHara - Are we live?</a></li>
<li><a href="https://www.scottohara.me/blog/2019/07/10/the-output-element.html">Scott OāHara - output: HTML's native live region element</a></li>
<li><a href="https://www.sarasoueidan.com/blog/testing-environment-setup/">Sara Soueidan - Setting up a screen reader testing environment on your computer</a></li>
<li><a href="https://adrianroselli.com/2023/04/exposing-field-errors.html">Adrian Roselli - Exposing Field Errors</a></li>
<li><a href="https://www.w3.org/TR/wai-aria/#attrs_liveregions">W3C - Live Region Attributes</a></li>
<li><a href="https://www.w3.org/TR/wai-aria/#live_region_roles">W3C - Live Region Roles</a></li>
<li><a href="https://webaim.org/projects/screenreadersurvey9/">Screen-reader User Survey #9 Results</a></li>
<li><a href="https://assistivlabs.com/articles/screen-reader-browser-pairing-stats">Which Screen Readers and Browsers are best for Accessibility Testing?</a></li>
<li><a href="https://business.scope.org.uk/article/accessibility-screen-readers-special-characters-and-unicode-symbols#:~:text=accessible%20for%20everyone.-,Use%20correct%20grammar%20and%20punctuation,text%20much%20easier%20to%20read.">How special characters and symbols affect screen reader accessibility</a></li>
</ul>
The Implied Web
2023-12-21T00:00:00Z
https://htmhell.dev/adventcalendar/2023/21/
by Halvor William Sanden<br><p>People donāt need call-to-action buttons. Interface elements made to get attention and <em>herd</em> people towards clicks increase cognitive effort because they obscure themselves and reduce interfaces to clickable surfaces.</p>
<p>The implied web is based on the idea that people read interfaces through the glasses of their needs combined with previous web platform experiences. Driven by tasks, they are open to clues about solutions and need clear communication to comprehend and actively move through the interface. In short, people must recognise elements so they can act accordingly.</p>
<p>To better understand how to work with the implied web, we will look at <em>signifier mishmash</em>, intent through HTML and the dynamics of roles.</p>
<h2 id="the-impossible-element">The impossible element</h2>
<p>Interface elements tasked with explicitly communicating clickability make things more complicated because clicking is neither a defining nor a driving factor in the interface; itās just the mundane way things work.</p>
<p>We make such click-focused elements by taking visual affordance indicators from elements we perceive as communicating clickability, usually the link and the button, and we mash them together. Affordance indicators are aspects of an element that communicate what kind of element they are. They can also indicate state and even hint at actions available to the user in the capacity of what they are. When we make a combined clickable style, maybe as part of a design system, we tend to reapply it to any relevant element. At the same time, we end up removing those elementsā native affordances.</p>
<p>The problem is that there are no affordance indicators or signifiers unique to the click, and HTML has no click element. In other words, itās impossible to communicate clickability as a standalone concept, and with good reason, telling people to click is a sign of inferior communication.</p>
<h3 id="action-isnt-functionality">Action isnāt functionality</h3>
<p>What signifies clickability for a link is not the same as for a button or a select. Signifiers belong primarily to elements; they are clues to the functionality of the element, not the action the user can take. The linkās functionality, for instance, is to point to somewhere, not to be clicked. If we apply its underline and pointer cursor to other elements, we communicate that those are also links or share some functionality instead of being the āclick hereā we wanted to convey.</p>
<p>Elements that donāt share default affordances, conventions and functions end up being too similar. It becomes difficult, if not impossible, to tell them apart.</p>
<h3 id="countering-attention">Countering attention</h3>
<p>When we approach elements as communicators of action, we also start adding weight to them based on how much attention we think they should get. Elements we want people to click, or think they would like to click, are made to draw attention to themselves. This comes at the cost of the surrounding elements. It can disrupt the order and make it harder to follow the flow of the interface.</p>
<p>As a countermeasure, we attempt to create a scale of clickability and a system of attention instead of an information hierarchy. We invent our own flavour of interface disconnected from HTML, web platform conventions and actual content or data. We end up with elements that donāt come across as anything in particular; we have effectively diverged communication into visuals and code.</p>
<h3 id="no-click-here">No click here</h3>
<p>We cannot work with the action and attention approaches in code. We must work with the meaning and state of elements and roles.</p>
<p>Even though there are ways to steal focus with code, there isnāt a āclick me!ā HTML element, and no attribute can create an attention hierarchy. What people can do is cycle through element types in various means of interaction, like keyboard navigation or screen readers. Still, attention can only be in one place at a time.</p>
<p>Also, action isnāt one thing. A click is not a click because an element is not an element; they have different functionality. For instance, a link can be alt-clicked, a label click activates its input, and a text input activates itself and enables the actual input functionality.</p>
<h2 id="meaning-through-html">Meaning through HTML</h2>
<p>Programming web interfaces is communication work. Itās not the codeās job to give life to a visual; itās the visualās job to reflect the meaning already there.</p>
<p>While HTML doesnāt have a visual side, we can safely talk about an elementās base visual properties as defined by conventions that make them reasonably similar across browsersā default CSS. They may have different styles, but most of the same affordance indicators are present, such as borders, underlines, hover changes and colours. We shouldnāt disregard an elementās conventions or break them without consideration because there is a direct link between the intentional use of the HTML element and its visuals.</p>
<p>Much of HTMLās meaning comes through roles, and disregarding them means working without the meaning that the web platform provides. In other words, we should work from the HTML elements, their conventions and default styles to bring the affordance indicators into the process. Then, we can consciously override them when necessary.</p>
<p>When the reason behind an HTML element lies in the usersā needs, and not a sketch, we go from writing valid code to writing correct code.</p>
<h3 id="roles">Roles</h3>
<p>Central to picking the correct HTML element is its role; thatās where a lot of meaning comes from.</p>
<p>Many HTML elements have implicit roles, and we donāt have to do anything but use the elements. Some roles can also change depending on non-ARIA attributes and ancestor element types. The mapping is done through <a href="https://www.w3.org/TR/html-aam/">HTML Accessibility API Mappings</a> (HTML-AAM). A role can be relevant for any means of consumption or interaction, even though the wide range of assistive tech perhaps comes to mind first.</p>
<p>Elements that have no implicit role are generic. That doesnāt mean we can use the elements anywhere; all HTML has designated usage areas. But some donāt need a role, and others cannot have one because the usage wouldnāt work well with things like assistive tech. For instance, <code>div</code>, <code>span</code> and <code>i</code> all have the generic role but different use cases and default visual properties. It would also be fairly annoying to have every <code>div</code> and <code>span</code> announced.</p>
<p>We cannot set the generic role, but we can set the role to <em>presentation</em> or <em>none</em>. In contrast to generic, the two are not exposed in accessibility APIs or accessibility trees.</p>
<p>We can also use the role attribute to communicate meaning HTML doesnāt have by default, though the role would no longer be implicit. Reasons to do this can be because the element is versatile, and it would be impractical to have a default role, or because we need to give an element a role different from its default. We should not explicitly set the same role already implied by the element.</p>
<h3 id="examples-of-implicit-and-indirect-roles">Examples of implicit and indirect roles</h3>
<p>The following examples are a few selected elements that demonstrate roles and how some can be affected by surrounding or related code.</p>
<ol>
<li><code>header</code> has the role of banner, and <code>footer</code> has the role of contentinfo. When we place them inside <code>article</code>, <code>aside</code>, <code>main</code>, <code>nav</code> or <code>section</code>, or elements that have set the role to article, complementary, main, navigation or region, their role becomes generic.</li>
</ol>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- header and footer with banner and contentinfo roles --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- header and footer with the generic role --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<ol start="2">
<li><code>section</code> has a generic role. When we give it an accessible name, typically using the aria-label attribute, it gets the role of region. Beware that over-use of named sections can cause clutter, but there can be other reasons to write code with these elements than exposing their roles.</li>
</ol>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- section with the generic role --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- section with the region role --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</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>changelog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<ol start="3">
<li><code>img</code> with an empty alt attribute has the role of presentation. <code>img</code> with text inside the alt attribute has the role of img. <code>img</code> without the alt attribute should be avoided, it has the role of img but it will not have an accessible name.</li>
</ol>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- img with the presentation role --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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><span class="token punctuation">"</span></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>/<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- img with the img role --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>horse with beard<span class="token punctuation">"</span></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>/<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- img with the img role but without an accessible name ā avoid! --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<ol start="4">
<li><code>select</code> has the role of combobox, but if we give it the multiple attribute or a size attribute greater than one, the role changes to listbox</li>
</ol>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- select with the combobox role --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- select with the listbox role --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">multiple</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">size</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">multiple</span> <span class="token attr-name">size</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<ol start="5">
<li><code>strong</code> and <code>em</code> have the roles of strong and emphasis. Strong is for important, serious or urgent, while emphasis is for just that. Both are misused to a level where their code semantics are no longer useful to screen readers and they are practically just a way to achieve bold and italic like the <code>i</code> and <code>b</code> elements. There can be other software that differentiates between them, so they are still relevant. Markdown and WYSIWYG editors are commonly not in that group and output only <code>strong</code> and <code>em</code> by default, further contributing to the misuse. And we canāt really blame WYSIWYG editors, seeing as they are more WYSIWYGIYCS ā <em>what you see is what you get if you can see</em>. The biggest contributor to misuse is probably the microscopic typographic difference. Leaving bold and strong for another article, we typically set things like titles in italic, while emphasis is used potentially to affect the meaning of an entire sentence.</li>
</ol>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- The author implies that something other than the movie is a better Elvis resource. --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>Elvis<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> is the best <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>em</span><span class="token punctuation">></span></span>movie<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>em</span><span class="token punctuation">></span></span> about Elvis.</span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- The author implies that other Elvis movies have other leading qualities, such as length or truth. --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>Elvis<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> is the <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>em</span><span class="token punctuation">></span></span>best<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>em</span><span class="token punctuation">></span></span> movie about Elvis.</span></code></pre>
<ol start="6">
<li>
<p><code>small</code>, <code>u</code>, <code>i</code>, <code>b</code>, <code>q</code>, <code>pre</code> are some of the elements with generic role, just like <code>span</code>. We should still use them as intended and not interchangeably, as they mostly have different default styling and layout behaviours.</p>
</li>
<li>
<p><code>menu</code> has the role of list. It does not have the role of menu or toolbar. Itās identical to <code>ul</code>; there is no inherent reason to use <code>menu</code> except if we want to separate the two in code. If we want to indicate navigation, chances are we already have a <code>nav</code> element with an aria-label. Additionally, the <code>menu</code> element can only have list items inside while <code>nav</code> element can hold complete lists and other elements like headings. The possible mixup with WAI-ARIAās menu role is reason enough not to use the <code>menu</code> element. The menu <em>role</em> is less about navigation and more about choices and actions like we find in toolbars.</p>
</li>
<li>
<p>Many specific input types, such as color, date, file and password have no implicit ARIA role. Inputs should have labels and attributes indicating the type and donāt need an additional role to convey meaning.</p>
</li>
<li>
<p><code>label</code> has no role and cannot be given an aria-label attribute since it is already a label for another element and cannot be anything else.</p>
</li>
<li>
<p>Setting aria-label or aria-labelledby is not allowed on several elements such as <code>a</code> without the href attribute, <code>div</code>, <code>p</code>, <code>abbr</code>, <code>address</code>, <code>body</code>, <code>caption</code> and <code>code</code>. Elements that cannot have aria-label or aria-labelledby can be given those if they change role, granted that they can change role. Even though the language might not allow something, it doesnāt mean browsers ignore it. Still, using labels on the mentioned elements could be a sign that another more specific element should be used instead or that something like a heading is missing.</p>
</li>
</ol>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- not allowed --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</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>news<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>crows<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Crows<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- allowed --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>article<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>news<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>crows.png<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>crows<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Crow picture<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="implied-platform">Implied platform</h2>
<p>While a button might be the desired destination of a page, clicking it doesnāt complete anyoneās task as long as the rest of the form is blank. Peopleās tasks lie at the single input asking for an email address, the form with a well of input types or a table heavy with data; thatās what we must communicate, the path and the information enabling people to walk it, not only the destination.</p>
<p>The implied web is not about avoiding being explicit; itās about leaning on the platform to tell browsers how it should work for people. Conventions and external consistency enable them to move across different services and domains more easily, and how we make our services should regard that.</p>
<p>Knowing about roles means knowing a central communication capability of the web. We can use it to help people read and comprehend the interface. We can use it to avoid rehashed elements, and we can use it to work with code, visuals and function simultaneously. Itās the direction in which the web moves, <em>again</em>.</p>
<h2 id="sources-and-resources">Sources and resources</h2>
<p><a href="https://html.spec.whatwg.org/multipage/">The HTML spec</a><br />
<a href="https://www.w3.org/TR/html-aria/">ARIA in HTML W3C recommendation</a><br />
<a href="https://www.tpgi.com/screen-readers-support-for-text-level-html-semantics/">Screen Readers support for text level HTML semantics by Steve Faulkner</a></p>
Design pattern for custom tooltips
2023-12-20T00:00:00Z
https://htmhell.dev/adventcalendar/2023/20/
by Jan Hellbusch<br><p>Should we use tooltips to convey information? Hints and descriptions are often included on web pages through tooltips ā but not everyone has access to them.</p>
<p>A tooltip is a short text that usually appears as a popup when a user hovers a mouse pointer over an element. Tooltips can be attached to any HTML element using the title attribute. There are only a few exceptions when a tooltip created with a title attribute will not be displayed (e.g., a title attribute on an iframe element).</p>
<p>In this article, weāll first take a look at the title attribute in HTML and point out to a few accessibility issues. Then weāll make a few general suggestions about how to use tooltips. In the third part, weāll build two accessible custom tooltips with HTML and ARIA, then look at two different CSS solutions and finally add an event handler in accordance with accessibility guidelines. Both tooltips can be previewed:</p>
<ul>
<li>In <a href="https://codepen.io/matuzo/debug/mdvGMpb">example 1</a>, youāll find a nested tooltip with a basic design using the z-index property.</li>
<li>In <a href="https://cdpn.io/pen/debug/RwvYZxX">example 2</a>, youāll find a tooltip using the new anchor positioning technique.</li>
</ul>
<h2>Accessibility issues for the title attribute</h2>
<p>The title attribute is not particularly accessible. As Steve Faulkner puts it:</p>
<figure class="u-mb">
<blockquote><p>If you want to hide content from mobile and tablet users as well as assistive tech users and keyboard-only users, use the title attribute.</p></blockquote>
<figcaption><cite><a href="https://www.tpgi.com/using-the-html-title-attribute-updated/">https://www.tpgi.com/using-the-html-title-attribute-updated</a></cite></figcaption>
</figure>
<p>Assistive technologies include screen readers and screen magnifiers. In terms of accessibility, title attributes lack support for the following user groups:</p>
<ul>
<li>Keyboard users. <p>Because keyboard users are only able to focus active elements such as links or forms, browsers can only reveal tooltips for focusable elements. Even then, browsers do not display the content of title attributes on keyboard focus (there were some exceptions for forms on Internet Explorer some time ago).</p></li>
<li>Screen reader users. <p>For active elements, browsers will process the content of a title attribute as an āaccessible descriptionā when an active element such as a link is focused with the tab key. Usually, the accessible description is suffixed to the link name. That can be helpful in certain situations, but screen reader users wonāt necessarily navigate by tab key, because non-focusable content will be skipped. A title attribute can nevertheless be discovered on links, forms and other active elements in a screen reader.</p></li>
<li>Screen magnification users. <p>Using a screen magnifier can make tooltips impossible to read. Tooltips included with a title attribute will only be displayed when the mouse pointer is hovering over the triggering element. Using screen magnification, the enlarged portion of the viewport that is actually visible can reduced significantly. In this situation, users will have to move the pointer to pan the tooltip to read the full text. However, as soon as the pointer hovers over the tooltip, browsers will hide the tooltip.</p></li>
</ul>
<p>Also take a look at the HTML Standard. HTML discourages the use of the title attribute:</p>
<figure class="u-mb">
<blockquote><p>Relying on the title attribute is currently discouraged as many user agents do not expose the attribute in an accessible manner as required by this specification (e.g., requiring a pointing device such as a mouse to cause a tooltip to appear, which excludes keyboard-only users and touch-only users, such as anyone with a modern phone or tablet).</p></blockquote>
<figcaption><cite><a href="https://html.spec.whatwg.org/multipage/dom.html#the-title-attribute">https://html.spec.whatwg.org/multipage/dom.html#the-title-attribute</a></cite></figcaption>
</figure>
<div class="highlight u-mb">
<p><strong>Best practice</strong>: do not use the title attribute to create a tooltip.</p>
</div>
<h2>Reconsidering using tooltips?</h2>
<p>If you are still considering using tooltips on your web pages, be restrictive when using them. There are plenty of guidelines on the web about when to use tooltips and when not, for example:</p>
<ol>
<li>Do not include crucial information in a tooltip. Information required to finish a task must always be accessible to all users.</li>
<li>Do not include repetitive or obvious text in a tooltip. At best, those kinds of tooltips are distracting.</li>
<li>Keep tooltips short. Tooltips can be a reminder or a brief rewording to a short label.</li>
<li>Tooltips should not cover other relevant content. For example, the tooltip should not cover the label in a form field.</li>
</ol>
<p>Most importantly, tooltips should only provide descriptive and non-essential text, giving slightly more detailed text for active elements such as links and form controls. Ultimately, they provide expendable text which is already on the web page.</p>
<p>The following code shows a form field with an additional text to be shown as a tooltip:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>foo<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"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">autocomplete</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>foo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>tooltip<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>Enter your first name followed by your surname<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>The hidden text is intended to be displayed when the form field is focused. The hidden text is the first step in creating a custom, accessible tooltip as an alternative to the title attribute. The CSS properties to disclose and hide the tooltip will be discussed in Step 2 below.</p>
<div class="highlight u-mb">
<p><strong>Best practice</strong>: provide only expendable information in a tooltip.</p>
</div>
<h2>Tooltip accessibility</h2>
<p>Make sure your tooltips are accessible. That will include keyboard users and users of assistive technology. An accessible tooltip will have to fulfil the following requirements:</p>
<ol>
<li>Use correct semantics (HTML and ARIA).</li>
<li>Make sure the tooltip can be displayed by keyboard.</li>
<li>Ensure all users can lightly dismiss the tooltip.</li>
</ol>
<p>Note that there is more to accessible custom tooltips than just these three aspects. For example, tooltips must also satisfy contrast requirements or adapt to text resizing.</p>
<h3>Step 1: HTML and ARIA</h3>
<p>If youāre starting from scratch, it is always best to begin with HTML. A tooltip for a link can be set up as follows:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://www.example.com<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> link text</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>tooltip<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>helpful, but non-essential tooltip<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>The tooltip does not have to be nested within the active element. What you need to consider in HTML is:</p>
<ul>
<li>Assistive technology must be able to determine the correct reading sequence. The tooltip must immediately follow the triggering component or its label.</li>
<li>You will need to position the tooltip. Nesting the tooltip within the link simplifies CSS positioning.</li>
</ul>
<p>Note: Users should never be able to focus something within a tooltip. If, for whatever reason, the popup requires focus, then you should be using a dialog element with the <code>showModal()</code> or <code>show()</code> methods. Alternatively, you might need a popovertarget attribute for the triggering element.</p>
<div class="highlight u-mb">
<p><strong>Best practice</strong>:</p>
<ul>
<li>When building a custom tooltip, first make sure the HTML makes sense in a screen reader.</li>
<li>Ensure the triggering element can be focused by keyboard.</li>
</ul>
</div>
<p>In our code example, the relationship between link and tooltip seems obvious, but the conveyed relationship must also be determinable by assistive technology. The places to check possible requirements are the <a href="https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/">ARIA Authoring Practices Guide (APG)</a> or the <a href="https://www.w3.org/TR/wai-aria-1.2/#tooltip">ARIA specification</a>, if the APG does not provide guidance for whatever you are coding. For tooltips, the APG suggests the following:
</p><ol>
<li>The element that serves as the tooltip container has a tooltip role.</li>
<li>The element that triggers the tooltip references the tooltip element with aria-describedby. To this end, the element with the tooltip requires an ID.</li>
</ol>
<p>Basically, with the <code>aria-describedby</code> attribute we are telling the link what its tooltip is and turning the tooltip into an accessible description for the link. Accessible descriptions for links are exposed to assistive technology users when the link is focused by tab key.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://www.example.com<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>me<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> link text </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>me<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>tooltip<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>helpful, but non-essential tooltip<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>How useful the tooltip role is when implementing custom tooltips is unclear. Currently, it makes no difference in assistive technology whether the tooltip has <code>role="tooltip"</code> or not. The relationship between link and tooltip is conveyed solely by the <code>aria-describedby</code> attribute. The role has purposely been omitted. Youāll find more on this subject on <a href="https://github.com/w3c/aria/issues/979">GitHub</a>.</p>
<div class="highlight u-mb">
<p><strong>Best practice</strong>: use ARIA only when it is useful to assistive technology.</p>
</div>
<p>Explicitly referencing a tooltip from its triggering element is even more important for form controls. Tooltips will not be nested within a component. To better position the tooltip, you might add a span element with a class as in the following snippet:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>foo<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"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>tooltipWidget<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">autocomplete</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>foo<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>bar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>bar<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>tooltip<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>Enter your first name followed by your surname<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<h3>Step 2: Displaying and hiding the tooltip</h3>
<p>For a tooltip to be accessible, displaying a tooltip must not only be possible for mouse users, but also for keyboard and touchscreen users. There are generally two approaches for displaying a tooltip with a keyboard:</p>
<ol>
<li>Automatically displaying the tooltip when the triggering element receives focus.</li>
<li>Providing a keyboard shortcut to open the tooltip for the focused element.</li>
</ol>
<p>The first approach makes tooltips easily discoverable for keyboard users, but if a webpage includes many tooltips, the continuous displaying of expendable information can become distracting. The advantage of the second approach to open the tooltip is that users can display the tooltip only on request. But the second approach also comes with some disadvantages:</p>
<ul>
<li>It is impossible to tell whether the tooltip will show supplemental (helpful) or expendable information. Hitting keyboard shortcuts to display (and hide) tooltips seems to be an unnecessary burden.</li>
<li>There is no standardized keystroke for displaying a tooltip. If you choose this technique, you should use a keystroke common to different platforms for invoking interaction such as space key using JavaScript.</li>
<li>Components with tooltips will require a visual affordance to indicate the presence of a tooltip. <p>Note: the visual affordance is for sighted users only and should not be accessible to screen readers. Screen readers access the tooltip through the <code>aria-describedby</code> attribute.</p></li>
</ul>
<p>We will take the first approach. This seems closer to the expected behavior for a tooltip.</p>
<p>There are two specific accessibility aspects to consider for your CSS:</p>
<ol>
<li>If tooltips appear on hover or keyboard focus, they must remain visible until hover or focus is removed by the user. Some users will have difficulty reading or even perceiving the displayed content, if the tooltip is hidden after a short time.</li>
<li>Especially users who use a screen magnifier with high magnification can only view small portions of the screen. In order to get an overview of the content, users must be able to pan the viewport, with the magnified portion following the mouse pointer. The tooltip must be displayed as long as the pointer is hovering on the triggering element as well as on the tooltip. See the second condition for success criterion 1.4.13 in the <a href="https://www.w3.org/WAI/WCAG22/Understanding/content-on-hover-or-focus">Web Content Accessibility Guidelines (WCAG) 2.2</a>. <p>Note: the magnification levels vary greatly and 20x, 30x or even higher magnification is quite possible. The higher the magnification level, the smaller the screen portion.</p></li>
</ol>
<p>Basic CSS for displaying and hiding a tooltip is fairly straightforward using a nested tooltip. The following CSS declarations create custom tooltips for the link and the input field for <a href="https://codepen.io/matuzo/debug/mdvGMpb">example 1</a>. You can use the position property to place the tooltip next to the triggering element:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">a:has(.tooltip), .tooltipWidget:has(.tooltip)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="token selector">.tooltipWidget:focus-within .tooltip, <br /><span class="highlight-line">.tooltipWidget:hover .tooltip,</span><br /><span class="highlight-line">.tooltipWidget .tooltip:hover,</span><br /><span class="highlight-line">a:focus .tooltip, </span><br /><span class="highlight-line">a:hover .tooltip, </span><br />a .tooltip:hover</span> <span class="token punctuation">{</span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span>block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">left</span><span class="token punctuation">:</span>0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">top</span><span class="token punctuation">:</span>1em<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span>0.5em 0.75em<span class="token punctuation">;</span> </span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span>#334E58<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span>solid #334E58 2px<span class="token punctuation">;</span> <span class="token comment">/* for contrast mode */</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span>10em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">z-index</span><span class="token punctuation">:</span>1<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p><a href="https://codepen.io/matuzo/pen/mdvGMpb">See example 1 on CodePen</a></p>
<div class="highlight u-mb">
<p><strong>Best practice</strong>: make sure to extend the hover state to the tooltip to allow users to move the pointer over the tooltip without the tooltip disappearing.</p>
</div>
<p>There is a lot more to consider when displaying tooltips, for example:</p>
<ul>
<li>Where should the tooltip be shown ā above, below or to one side of the triggering element?</li>
<li>What is to be done if the tooltip is cut off by the viewport?</li>
<li>Can we display tooltips using CSS or do we need JavaScript?</li>
<li>Do you need some kind of package to create your custom tooltips?</li>
</ul>
<p>As this article deals with the accessibility of tooltips, we wonāt go down a rabbit hole to provide answers to these questions. The bottom line is that as soon as you need a flexible presentation of tooltips (e.g., displaying tooltips below the triggering element, unless the triggering element is at the bottom of the viewport, in which case the tooltip should be displayed above the triggering element), you are dependent on JavaScript. You may need to exchange classes for repositioning or redimensioning your tooltip, depending on viewport size and other factors. Your code can get quite complicated. See some assessments on: <a href="https://developer.chrome.com/blog/tether-elements-to-each-other-with-css-anchor-positioning/">Tether elements to each other with CSS anchor positioning</a>.</p>
<p>But fortunately, there is something new in CSS that will help solve these issues.</p>
<p>The new CSS feature is <a href="https://www.w3.org/TR/css-anchor-position-1/">anchor positioning</a>. Currently, it will work in Chrome Canary with experimental mode switched on, but it will take some time before it is found in mainstream browsers.</p>
<p>Anchor positioning promises flexible positioning of your tooltips based on CSS and your guidance. First of all, you are not dependent on nesting your tooltips to easily position them. You can rearrange your HTML, but remember that you still need to consider the reading order. The input field from example 1 could be adapted to the following (<a href="https://cdpn.io/pen/debug/RwvYZxX">example 2</a>):</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>form-element<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>foo<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"></</span>label</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">autocomplete</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>foo<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>bar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>bar<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>tooltip<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Enter your first name followed by your surname</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span></code></pre>
<p>With anchor positioning, the example could be adapted to display the tooltip to the right or below the anchor (triggering element), depending on where the anchor is in the viewport:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">anchor-name</span><span class="token punctuation">:</span> --anchor-foo<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">*</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token selector">input</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 200px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 1px solid #334e58<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">anchor-name</span><span class="token punctuation">:</span> --anchor-foo<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> </span><br /> <span class="token selector">input:hover,<br /> input:focus</span> <span class="token punctuation">{</span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 2px solid #334e58<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 0.5rem 0.75rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token selector">.form-element</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> </span><br /> <span class="token selector">.form-element:hover ~ .tooltip,<br /><span class="highlight-line"> .form-element ~ .tooltip:hover,</span><br /> .form-element:focus-within ~ .tooltip</span> <span class="token punctuation">{</span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token selector">.tooltip</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">margin-right</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> #334e58<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 2px solid #334e58<span class="token punctuation">;</span> <span class="token comment">/* for contrast mode */</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">min-width</span><span class="token punctuation">:</span> <span class="token function">anchor-size</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> --anchor-foo width</span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Reposition a little bit earlier */</span></span><br /><span class="highlight-line"> <span class="token property">anchor-default</span><span class="token punctuation">:</span> --anchor-foo<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">position-fallback</span><span class="token punctuation">:</span> --left-to-bottom<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token atrule"><span class="token rule">@position-fallback</span> --left-to-bottom</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token atrule"><span class="token rule">@try</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">bottom</span><span class="token punctuation">:</span> <span class="token function">anchor</span><span class="token punctuation">(</span>bottom<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">anchor</span><span class="token punctuation">(</span>right<span class="token punctuation">)</span> + 0.5rem<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token atrule"><span class="token rule">@try</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">anchor</span><span class="token punctuation">(</span>bottom<span class="token punctuation">)</span> + 0.5rem<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token function">anchor</span><span class="token punctuation">(</span>left<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">anchor-size</span><span class="token punctuation">(</span>--anchor-foo width<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span></code></pre>
<p><a href="https://codepen.io/matuzo/pen/RwvYZxX">See example 2 on CodePen</a></p>
<p>There are numerous further properties provided in anchor positioning. You can find an introduction by Sebastian Weber on: <a href="https://blog.logrocket.com/use-css-anchor-positioning/">How to use CSS anchor positioning</a>.</p>
<h3>Step 3: Tooltip dismiss</h3>
<p>Light dismiss is a term used in the HTML Standard. Light dismiss means that clicking outside of a popover of any sort will close the popover. For keyboard users, light dismiss is equivalent to pressing the Escape key.</p>
<p>Above, we mentioned the second condition in success criterion 1.4.13 in WCAG 2.2. The success criterion has a total of three conditions. When applied to tooltips, the first condition must also be met for the tooltip to be considered accessible:</p>
<figure>
<blockquote><p>A mechanism is available to dismiss the additional content without moving pointer hover or keyboard focus, unless the additional content communicates an input error.</p></blockquote>
</figure>
<p>Keyboard users also using some form of magnification need a way to clear their viewport from unwanted tooltips. Because of the magnification, tooltips and corresponding triggers can fill a major part of the viewport. They must be able to hide the tooltips by keyboard, for example by pressing the Escape key. Simply moving focus forwards and backwards wonāt help.</p>
<p>To prevent interference through tooltips, there are two possible techniques:</p>
<ol>
<li>The tooltip is positioned so that it does not cover any other content.</li>
<li>You provide a mechanism to dismiss the tooltip by keyboard.</li>
</ol>
<p>Even if a tooltip does not obscure any other content, it is desirable for the second technique to be applied. In other words, the tooltip must stay visible as long as hover or focus is on the triggering element and the user has not explicitly dismissed it. Only error messages may be non-dismissible (e.g., when remedial action is necessary).</p>
<p>For keyboard users, pressing the Escape key while focus is on the triggering element should hide the tooltip again. We can go back to both of the examples and add the following two event handlers to do that:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">let</span> tooltips <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"[aria-describedby]"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">tooltips<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">tooltip</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> tooltip<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></span><br /><span class="highlight-line"> <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></span><br /><span class="highlight-line"> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> tooltip<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-describedby"</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">.</span>style<span class="token punctuation">.</span>display <span class="token operator">=</span> <span class="token string">"none"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> tooltip<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"blur"</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></span><br /><span class="highlight-line"> document</span><br /><span class="highlight-line"> <span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span>tooltip<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-describedby"</span><span class="token punctuation">)</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">.</span><span class="token function">removeAttribute</span><span class="token punctuation">(</span><span class="token string">"style"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<div class="highlight u-mb">
<p><strong>Note:</strong> by hiding the tooltip directly in the DOM, the style property remains persistent, meaning the tooltip will not be displayed a second time. To prevent that from happening, the style property is removed when the triggering element is not focused anymore.</p>
</div>
<div class="highlight u-mb">
<p><strong>Best practice</strong>: ensure a custom tooltip can be dismissed by keyboard.</p>
</div>
<h2>Acknowledgements and links</h2>
<p>Thanks to <a href="https://www.textformer.de/">Nicolai Schwarz</a> and <a href="https://word-nerd.eu/">Tessa Hellbusch</a> for their support.</p>
<p>Links referenced in the article:</p>
<ul>
<li>Using the HTML title attribute ā updated March 2020 (Steve Faulkner) <br /><a href="https://www.tpgi.com/using-the-html-title-attribute-updated/">https://www.tpgi.com/using-the-html-title-attribute-updated/</a></li>
<li>HTML Standard, 3.2.6.1 The title attribute (WHATWG) <br /><a href="https://html.spec.whatwg.org/multipage/dom.html#the-title-attribute">https://html.spec.whatwg.org/multipage/dom.html#the-title-attribute</a></li>
<li>ARIA Authoring Practices Guide (W3C) <br /><a href="https://www.w3.org/WAI/ARIA/apg/">https://www.w3.org/WAI/ARIA/apg/</a></li>
<li>Accessible Rich Internet Applications (WAI-ARIA) 1.2 (W3C) <br /><a href="https://www.w3.org/TR/wai-aria-1.2/">https://www.w3.org/TR/wai-aria-1.2/</a></li>
<li>Clarify the use of role=tooltip <br /><a href="https://github.com/w3c/aria/issues/979">https://github.com/w3c/aria/issues/979</a></li>
<li>Understanding Success Criterion 1.4.13: Content on Hover or Focus (W3C) <br /><a href="https://www.w3.org/WAI/WCAG22/Understanding/content-on-hover-or-focus">https://www.w3.org/WAI/WCAG22/Understanding/content-on-hover-or-focus</a></li>
<li>Tether elements to each other with CSS anchor positioning (Jhey Tompkins) <br /><a href="https://developer.chrome.com/blog/tether-elements-to-each-other-with-css-anchor-positioning/">https://developer.chrome.com/blog/tether-elements-to-each-other-with-css-anchor-positioning/</a></li>
<li>CSS Anchor Positioning (W3C) <br /><a href="https://www.w3.org/TR/css-anchor-position-1/">https://www.w3.org/TR/css-anchor-position-1/</a></li>
<li>How to use CSS anchor positioning (Sebastian Weber) <br /><a href="https://blog.logrocket.com/use-css-anchor-positioning/">https://blog.logrocket.com/use-css-anchor-positioning/</a></li>
</ul>
Boosting testing efficiency: how semantic HTML transforms End-to-End testing
2023-12-19T00:00:00Z
https://htmhell.dev/adventcalendar/2023/19/
by Stefania Mellai<br><p>Semantic and accessible HTML serves as a powerful tool, enhancing not only human interaction but also the efficiency of software systems. For instance, when users fill out forms with clear labels and accessible input fields, this reduces errors and ensures sending accurate data to the backend and databases. You may also have heard about how a well structured web page is fundamental for search engine optimization (SEO).<br />
Creating crafts that are accessible and easy to use for everybody should be the top concern of every developer. The good thing is that prioritizing this aspect can also have a positive side effect: it enables seamless interactions between your website and other software components.<br />
Let's explore in particular how to improve your testing processes and save valuable time.</p>
<!-- Manuel:
1. "enhancing not only human interaction but also the efficiency of software systems" <- can we get some examples, please?
2. "equally vital" <- Important? yes! Equal with general access to users? I don't knowā¦
3. "often-overlooked" <- examples?
-->
<!-- Saptak: I agree with Manuel, I also feel making things easier for people is more important than seamless interactions between software parts. Though seamless interaction is related to better usability for people hence it is very important to test that, but I think the phrasing of the sentence makes it feel like end-user usability is undermined a little. -->
<!-- Stefania: Thanks for this input, I tried to add examples and rephrase the problematic part -->
<h2 id="the-connection-between-semantic-html-accessibility-and-testing">The Connection Between Semantic HTML, Accessibility, and Testing</h2>
<p>This topic was beautifully explored by Rita Castro, in her talk <a href="https://portal.gitnation.org/contents/a11y-and-tdd-a-perfect-match">a11y and TDD: A Perfect Match</a>. While Rita mainly focuses on unit tests, it's worth mentioning how these principles can be applied to end-to-end testing as well, and for the same reasons.</p>
<h2 id="the-end-to-end-testing-dilemma">The End-to-End Testing Dilemma</h2>
<p>End-to-end testing, goes beyond checking individual parts of a website. It tries to act like a real user in a real web browser, testing the whole webpage as it would appear to someone using it for real.</p>
<p>Let's imagine we are writing a unit test for a list item component, that produces the following HTML:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> A description for this item</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Pick this item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span></code></pre>
<p>And the unit test can target the button like this:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">screen<span class="token punctuation">.</span><span class="token function">getByRole</span><span class="token punctuation">(</span><span class="token string">'button'</span><span class="token punctuation">)</span></span></code></pre>
<p>This approach is great for testing the component in isolation, which is what "unit" means. It simplifies testing by not worrying about other possible buttons on the page and by ignoring context.</p>
<p>However, unit tests can't fully replicate real-life scenarios. For instance, they can't ensure that clicking the button saves some data for the current user and navigates to another page.</p>
<p>Now, consider our list item used in a full web page, with a topbar that contains buttons and other sections beyond the list container. The list in the page wasn't built with semantics and accessibility in mind, so it looks like this (taken from real production web application page):</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>root<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- topbar --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Page Title<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>Page subtitle<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- a form for filtering the list --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>hr</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>A description for this item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span> Pick this item <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- second item --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- ... --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span></code></pre>
<!-- Manuel: There's a header, an h1, a p, a button. Looks pretty smenativ to me. At least I've seen worse. -->
<!-- Stefania: True. Replaced with: "The list in the page wasn't built with semantics and accessibility in mind" -->
<p>Most end-to-end testing tools can record your actions as you interact with a webpage and then use those actions for automation. They save selectors to automatically identify the items you interact with.<br />
We want to test the click of the button in the first element in the list. I have to confess I simplified a bit the HTML in the example, and the real selector produced by the tool looks like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">//*[@id="root"]/div/div/div/div/div/div/div[3]/div/div/div[2]/div[1]/div[2]/button</span></code></pre>
<p>Whenever you tweak your HTML, even adding or removing only one of the div wrappers, your test suite can easily get messed up in this situation. End-to-end tests aren't something you update every day because they are slow and need lots of resources. So, figuring out why a test failed can be time-consuming and take you away from more important tasks.</p>
<p>Testing tools are getting more and more better at identifing targets, but the algorithms are not bulletproof and can't react to page structure changes with 100% reliability. That's why it is important that targets can be recognized without ambiguity. One of the techniques more common to obtain this clarity is adding unique <code>id</code>s, <code>test-id</code>s or other selectors to the HTML elements, but it's not a best practice, for a bunch of reasons:</p>
<ul>
<li><strong>Separation of Concerns</strong>: Mixing testing concerns with your application's business logic violates the principle of separation of concerns. You are coupling two distinct and indipendent objects and a person working in one of the sides may be totally unaware that something is used and necessary on the other side. This person can also be the same person that didn't work on one of the parts for some time and lose the context in the meanwhile.</li>
<li><strong>Maintainability</strong>: Test IDs may need to change or evolve as your application grows and changes. When you embed these IDs directly in your code, you may need to make widespread changes throughout your codebase whenever test requirements change. This can be time-consuming and error-prone.</li>
<li><strong>Code Clutter</strong>: Embedding test-related identifiers in your source code can clutter the codebase and make it less readable for developers.</li>
<li><strong>Accessibility</strong>: Adding test IDs actively misses the opportunity to incorporate accessibility testing for free. If my test suite explicitly looks for a button, it also ensures that the target keeps being an accessible button, and it's not refactored to unsemantic div later.</li>
</ul>
<!-- Manuel: "your test suite can easily get messed up" <- Instead of saying "use less divs", shouldn't testing tools get better at locating items on the page?-->
<!-- Stefania: You're right, the problem here wasn't having too many divs, but rather having an unsemantic list, hope I clarified that -->
<!-- Saptak: As far as my experience with end-to-end testing, I usually write the selectors myself (which I don't know is the best practise or not). I always use class/ids in the elements and then query using them while trying to find an element on the page with it's xpath? I am not sure how writing semantic HTML will help with this automatic selectors. Won't tweaking the HTML even with semantic HTML mess up the test suite? -->
<!-- Stefania: hope I explained the first part above.
"I am not sure how writing semantic HTML will help with this automatic selectors." -> agree, it probably want, I wanted to suggest to switch to manual selector instead, I'm adding this clarification.
"Won't tweaking the HTML even with semantic HTML mess up the test suite?" -> sure thing, but you were supposed to have semantic HTML in first place, and adjusting the HTML is bug fixing and your test are supposed to adapt only this time and ensure the HTML stays semantic later.
-->
<h2 id="the-power-of-semantic-html-in-testing">The Power of Semantic HTML in Testing</h2>
<p>Now, envision a different scenario where your list is built with clean, semantic HTML. We don't use the automatic targeting algorithm and we add a manual selector for the first item in the list like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">//*li[1]/button</span></code></pre>
<!-- Manuel:
1. In the previous example you used a different syntax. This one looks like CSS.
2. That example looks unrealistic. At least, some of the divs must have been there for a reason. Also, the presence of a lot of divs doesn't mean that your code isn't clean and semantic. You can have both, semantic HTML and a lot of divs. -->
<!-- Stefania:
1. fixed, using same semantic
2. unfortunately it's something that happened to me and it's a page that we have in production. In particular I needed to deploy a change across several places, and for this reason, in this place only, I got blocked for about a week. Big part of all of this makes sense only for situations similar to what we have in my company or bigger companies, with >80 engineers and also separated QA people and therefore lot of complexity. Of course for a simple web page doesn't make any sense to have e2e tests at all. The point here is that having a selector similar to this //*[@id="root"]/div/div/div/div/div/div/div[3]/div/div/div[2]/div[1]/div[2]/button is very fragile, and better to use manual target instead. But the latter would be easy and possible only with good page structure and semantic HTML.
-->
<p>Regardless of any HTML changes you make to the sourrounding structure around the list item, your test remains rock-solid. To recap:</p>
<ul>
<li>Relying on automatic targeting algorithm may result in flakyness and, in best case scenario, you need to regenerate the target. If your tests require adjustements every time you change the underlying structure of your markup, they may not authentically simulate a user journey but instead are too closely tied to implementation details.</li>
<li>Using arbitrary <code>ids</code> or other unique selectors is a bad practice.</li>
<li>Having semantic HTML enables easy manual targeting that is more resilient against changes. It should also help detecting actions that may break the accessibility of your UI.</li>
</ul>
<!-- Manuel: Not regardless of _any_ HTML. If I wrap the button in an element, the test won't work anymore, right? -->
<!-- Stefania: rephrased -->
<!-- Saptak: I am little confused with the code example since there is a mix of HTML, CSS and some end-to-end testing code. For example, in this section, I have no idea what is the HTML structure, and how that is helping me write end-to-end testing. I feel some better example with the semantic HTML structure as well as an example testing code will help with solidifying the point you are trying to make. Like can you give an example of the selector automatically generated with end-to-end testing tool for a page with more semantic HTMLs and how that is better than the previous selector in being more reliable? -->
<!-- Stefania: I tried to explain that further in the bulletpoint recap above -->
<h2 id="why-it-matters">Why It Matters</h2>
<p>So, why should you care about semantic HTML and accessibility in testing?</p>
<!-- Manuel: I still haven't figured out what accessibility does for testing? I kinda understand what you mean by semantic here, but which part does accessibility play? -->
<!-- Stefania: hope this is more clear now -->
<ul>
<li><strong>Developer and QA Bliss</strong>: Life becomes easier for developers and QA engineers. Writing tests becomes a breeze when you can work with straightforward, semantic code.</li>
<li><strong>Mimicking Real Users</strong>: Your tests better mimic real user behavior. Real users don't look for divs and spans in your web application; they look for headings, buttons, links, and familiar interface elements with clear labels.</li>
</ul>
<!-- Manuel: Users aren't concerned with complex div hierarchies, but they aren't conserned with h2s, h3s, <button>, <a>, or any HTML either. -->
<!-- Stefania: What I mean is that a user may know zero about HTML, but they know what a button, a link or a text input field is, and they know how they can usually interact with them and what to expect. I think screen readers users are also well aware of what haading level 1 or 2 are -->
<ul>
<li><strong>Reliability and Time Savings</strong>: Your tests become more reliable and won't break with every deployment for arbitrary reasons. Say goodbye to countless hours spent tweaking tests and hello to more productive work.</li>
</ul>
<!-- Manuel: I don't know anything about testing, but again, that sounds like the testing tool is shit if a minor change to the structure breaks everything. -->
<!-- Stefania: Think about how hard we find producing accessible interface and how inaccessible is the web, why is that? Assistive technologies users have another layer of software between them and your interface. Interacting with machines is way harder than interacting with humans and that's why AI is so limited as well. Humans can fill the gaps when facing missing information because we are so good at pattern recognition and we are clever, machines are not, they are very stupid and they need very clear instructions. so also for this reason there is a strong relationship between testing and a11y, they partially suffer from the same issues and e2e testing in particular, because it's supposed to simulate real user behavior, but it's a very limited simulation. so yes, it's real shit :D -->
<p>In conclusion, embracing semantic HTML and accessibility in your development process isn't just about adhering to best practices; it's a game-changer for testing. It streamlines your testing efforts, makes your tests more realistic, and ensures they stand the test of timeāsaving you precious hours and frustration. So, don't underestimate the power of clean code; it could be your testing superhero in disguise.</p>
The road to HTMHell is paved with semantics
2023-12-18T00:00:00Z
https://htmhell.dev/adventcalendar/2023/18/
by Vadim Makeev<br><p>HTML semantics is a nice idea, but does it really make a difference? Thereās a huge gap between HTML specās good intentions and what browsers and screen readers are willing to implement. Writing semantic markup only because <a href="https://youtu.be/EIBRdBVkDHQ">the good spec is a spec, and it is good, and itās a spec</a> is not the worst approach you can take, but it might lead you to HTMHell.</p>
<h2 id="simple-days">Simple days</h2>
<p>Like most people involved in the front-end, I started my journey into Web development from HTML. It was simple enough, close to a natural language, and easy to use: you type some tags, save a text file, and reload the browser to see the result. And it would almost never fail if I made a mistake!</p>
<p>Back then, I considered HTML a simple set of visual building blocks. It was too late for purely visual <code><font></code> elements (the CSS has replaced them), but the general idea stayed pretty much the same: if you wrap your text into <code><h1></code>, it becomes big and bold, if you have two <code><td></code> cells in a row, thatās your two-column layout. Easy! I learned tags to be able to achieve certain styles and behaviors. Remember <code><marquee></code>?</p>
<marquee id="marquee" behavior="alternate" scrollamount="7">
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>marquee</span><br /><span class="highlight-line"> <span class="token attr-name">behavior</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">scrollamount</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><br /><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>marquee</span><span class="token punctuation">></span></span></code></pre>
</marquee>
<p style="text-align: center">
<button type="button" onclick="document.getElementById('marquee').stop()" style="all: revert">
Stop Marquee
</button>
<button type="button" onclick="document.getElementById('marquee').start()" style="all: revert">
Start Marquee
</button>
</p>
<script>
if (matchMedia('(prefers-reduced-motion)').matches) {
document.getElementById('marquee').stop();
}
</script>
<p>That was just the beginning: soon, I needed calendars, popups, icons, etc. It turned out I had to code them myself! And so I did, mainly using divs, spans, and some CSS. Back in the mid-2000s, there werenāt any particular ālogicalā tags or functional widgets, only the ones youād find on a typical text editor panel.</p>
<p>But at some point, a trend called ā<a href="https://www.webstandards.org/">web standards</a>ā emerged: it suggested to stop using HTML as a set of visual blocks and start thinking about the meaning of the content and wrapping it into appropriate tags: <code><table></code> only for tabular data, not layout; <code><blockquote></code> only for quotes, not indentation, etc. The people bringing the web standards gospel were convincing enough, so I joined the movement.</p>
<h2 id="semantics">Semantics</h2>
<p>Following the trend, we started studying the HTML 4 spec to learn the proper meaning of all those tags weāve already known and many new ones weāve never heard about. Suddenly, weāve discovered semantics in HTML, not just visual building blocks.</p>
<ul>
<li><code><b></code> and <code><i></code> werenāt cool anymore: the proper stress and emphasis could only be achieved with <code><strong></code> and <code><em></code>.</li>
<li><code><ul></code> and <code><ol></code> werenāt only for bulleted/numbered lists in content anymore, but for all kinds of UI lists: menus, cards, icons.</li>
<li><code><dl></code>, <code><dt></code>, <code><dd></code> were accidentally discovered in the spec and extensively used for all kinds of lists with titles.</li>
<li><code><table></code> was banned from layout usage mainly because it wasnāt meant for that by the spec, but later, we also discovered rendering performance reasons.</li>
</ul>
<p>Why? Because we started paying attention to the spec, and it was semantically correct to do so. Every decision we make would have to be checked to determine whether itās semantic enough. And how would we do that? By reading the spec like itās a holy book that gives you answers in challenging moments of your life. On top of that, there was the <a href="https://validator.w3.org/">HTML Validator</a>ās seal of approval.</p>
<p><img src="https://htmhell.dev/adventcalendar/2023/18/images/valid.svg" alt="W3C HTML 4.01 badge with a checkmark." /></p>
<p>But then came the <a href="https://en.wikipedia.org/wiki/Cambrian_explosion">Cambrian explosion</a> that changed everything: HTML 5.</p>
<h2 id="a-new-hope">A new hope</h2>
<p>Just after the failed promise of <a href="https://en.wikipedia.org/wiki/XHTML">XHTML</a>, HTML 5 brought us new hope. Many new elements were added based on existing naming conventions to pave the cow paths. The new spec has challenged browsers for years ahead, from supporting the new parsing algorithm to default styles and accessibility mappings.</p>
<p>For the Web standards believers of the old spec, the new one was just a promised land:</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/blog/aria-accessibility-html-landmark-roles/">Landmarks</a> to mark logical parts like headers, footers, asides, navigations, sections, and articles.</li>
<li>Variety of new form elements other than the text ones: <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date">dates</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email">emails</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number">numbers</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range">ranges</a>, and <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/color">colors</a>.</li>
<li>Media and interactive elements for <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video">video</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio">audio</a>, and <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture">graphics</a>.</li>
</ul>
<p>There was even a logo for semantics in the <a href="https://www.w3.org/html/logo/">HTML 5ās design</a>!</p>
<p><img src="https://htmhell.dev/adventcalendar/2023/18/images/semantics.svg" alt="Semantics logo with three horizontal angled lines pointing up." /></p>
<p>Apart from extending the list of functional building blocks, the spec added several semantic elements that didnāt even come with any styling, just meaning. But not only that! Some old, purely visual elements were lucky enough not to be deprecated but redefined. For example, <code><b></code> and <code><i></code> became cool again, though no one could explain the use cases, apart from rather vague taxonomy and emphasis ones and⦠naming ships. You think Iām kidding? <a href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-i-element">Check the spec!</a></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>Boaty McBoatface<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span></code></pre>
<p>Donāt get me wrong, I think HTML 5 significantly advanced the Web, but it has also detached us from reality even further. Especially the idea of an outline algorithm and multiple nested <code><h1></code> elements that would change the level based on nesting. It was never implemented by any browser but existed in the spec for a long, long time <a href="https://github.com/whatwg/html/pull/7829">until finally removed in 2022</a>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Please<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Donāt use<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>This code!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>ā ļø Please donāt use the code above. Itās wrong and harmful.</p>
<p>Personally, Iāve wasted too many hours arguing about the difference between <code><article></code> and <code><section></code> for purely theoretical reasons instead of focusing on good user experience.</p>
<h2 id="drunk-on-semantics">Drunk on semantics</h2>
<p>Although the spec would provide examples, it primarily focused on marking up content, not UI. Even examples themselves were often purely theoretical with a kind of usage that would be semantically correct, not always practically useful. Thereās <a href="https://www.w3.org/wiki/HTML/W3C-WHATWG-Differences">another whole story</a> about the difference between the W3C and WHATWG spec versions, but the W3Cās examples were usually better.</p>
<p>Iāve seen a lot of weird stuff and did it myself, too. People would often look at the HTML spec as a dictionary, looking up a word in the list of elements for an idea they had in mind. Try to read the following examples through the eyes of a beginner, giving a shallow look at the spec. They totally make sense!</p>
<ul>
<li><code><menu></code> for wrapping the navigation menus.</li>
<li><code><article></code> for the content of an article.</li>
<li><code><input type="number"></code> for a phone number.</li>
<li><code><button></code> for everything that looks like a button.</li>
</ul>
<p>I havenāt seen the <code><slot></code> element used on a casino website to mark up a slot machine, but maybe only because Iām not into gambling. But the rest of the examples are real.</p>
<p>At the same time, a lot of people would read the spec carefully and use <code><footer></code>, <code><header></code>, <code><main></code>, and other semantic elements properly. But the reason for that wonāt be any different: they would also aim for semantically correct markup only because the spec says so. And if it does, the smartest of us would think it should be good for users, search engines, etc. Right?</p>
<p>It turned out that the spec could be wrong, and <strong>semantically correct markup wouldnāt guarantee good practical results</strong>.</p>
<p>I donāt blame people who gave up on following the spec altogether and became cynical enough to use <code><i></code> for icons instead of naming damn ships. Fortunately, I didnāt go this way. I found another reason to keep caring about markup: user experience and accessibility.</p>
<h2 id="good-intentions">Good intentions</h2>
<p>Unlike many other languages, HTML is a user-facing one. It means that <strong>our decisions directly affect users</strong>.</p>
<p>Fortunately, it doesnāt matter how we format our markup, but our selection of elements matters a lot. So when I hear āthis markup is semantic,ā it often means that itās correct according to the spec but not exactly good for actual users. Even though both can be true at the same time, the focus is in the wrong place.</p>
<p>It seems to me that we decided to trust the specās recommendations at some point without checking whether they were true. I firmly believe that the spec authorsā intentions are always good, and I know many smart people working on the HTML spec. But when it comes to implementation in browsers or screen readers, these intentions donāt always survive the reality.</p>
<p>There are usually three main obstacles:</p>
<ol>
<li>Product priorities: you probably know that already, but accessibility isnāt always a number one priority for various reasons, including complexity and the lack of people who know the area.</li>
<li>Different points of view: for the same reason, automated testing wonāt save you from accessibility issues, different user agents might have other points of view on certain platform features.</li>
<li>Actual user experience: browsers call themselves āuser agentsā for a reason. When a specific platform feature or how developers use it hurts the users, browsers tend to intervene.</li>
</ol>
<p>For example, the following list wonāt be exposed as a list to VoiceOver in Safari only because you decided to disable default bullets and implement custom ones via CSS pseudo-elements.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">list-style</span><span class="token punctuation">:</span> none</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>You can force the usual behavior by adding <code>role="list"</code> to every list you style, but how convenient is that? Not at all for you as a developer. But Safari has probably had some reasons, most likely to improve their usersā experience by ignoring all semantically correct lists we started using so much outside of content.</p>
<p>As for the screen readers, Steve Faulknerās ā<a href="https://www.tpgi.com/screen-readers-support-for-text-level-html-semantics/">Screen Readers support for text level HTML semantics</a>ā article might open your eyes to the actual value of those tags weāre so passionately arguing about.</p>
<blockquote>No browsers expose <code><strong></code> or <code><em></code> element role semantics in the accessibility tree.</blockquote>
<p>Again, you can force some semantics via ARIA roles, but should you? Thatās an open question. The answer depends on the value youāre trying to bring your users.</p>
<p>Does it mean we should immediately stop using semantic elements if they donāt bear any value for the users? I donāt think so. But I stopped using a <em>semantics argument</em> when talking about good markup. Just like tabs and spaces, semicolons, or quotes, semantics sometimes is a stylistic preference.</p>
<p>Thereās also a future-proofing argument that suggests using semantic markup with the hope that someday, browsers will start supporting all those elements they choose to ignore now. I wouldnāt rely on it too much and prefer to focus on whatās important right now.</p>
<p>I used to be among those people whoād judge the quality of a website based on the number of divs itās built of. Weād say, āNah, too many divs, itās not semantic.ā Now I know that <strong>whatās inside of those divs matters the most</strong>. Enough landmarks, headings, links, and buttons would make it good, even if the divs/semantic elements ratio is 1000 to 10. We are <em>divelopers,</em> <a href="https://twitter.com/chriscoyier/status/1050456501414838272">as Chris Coyier once said</a>. Donāt be ashamed of this, wear this name with pride.</p>
<h2 id="training-wheels">Training wheels</h2>
<p>Following specās recommendations with semantic markup is still a good start, especially when you treat it as not just the list of available elements. I mostly agree with this idea often expressed by accessibility experts:</p>
<blockquote>If you write semantic markup, it will be mostly accessible.</blockquote>
<p>But to me, it sounds like a simple answer to a complex question. The HTML spec might be a good set of training wheels, but at some point, youāll have to take them off. Not everything can be solved by semantic markup, for example, youāll need to learn ARIA to create any modern interactive UI. Thereās just not enought semantic elements for everything!</p>
<p>There are many simple answers waiting for you in the spec or articles praising semantics as the only thing you need. There are even more compromises made in modern frameworks in the name of better developer experience. And they arenāt all wrong! But if you keep your focus on the user experience, on the actual quality of the user interface, youāll be able to make the right decisions.</p>
<p>And you know what? It doesnāt matter if you agree with me on the value of semantics. Iām sure youāll be fine. After all, youāve just read a big rant on HTML in the HTMHell advent calendar.</p>
Revisiting Fundamentals - Semantic lists for Improved Accessibility
2023-12-17T00:00:00Z
https://htmhell.dev/adventcalendar/2023/17/
by Winnie Bosibori<br><p>Lists are one of the fundamental semantic HTML configurations that, when implemented appropriately can enhance accessibility.</p>
<h2 id="html-lists-refresher">HTML Lists Refresher</h2>
<p>Whenever I visit any website, I have formed the habit of checking for any accessibility issues and delving deeper into the HTML code structure. One apparent thing is that a large number of sites are characterized by non-semantic code structures that result in inaccessible sites.<br />
One common culprit that I constantly come across is poorly marked-up list structures. From having list elements made with the paragraph elements to having headings, div, or span elements as direct children of <code><ul></code>, <code><ol></code>, or <code><dl></code> elements.</p>
<p>As developers or designers, we may sometimes overlook and deem lists as insignificant HTML constructs. However, Without lists, it will be difficult for people to follow content or information on a site. This is because they facilitate grouping related pieces of information, so they are associated with each other and make it easy to read and understand. In addition, people who use assistive technologies such as screen readers may be able to easily interact with a page using <a href="https://dequeuniversity.com/screenreaders/survival-guide">keyboard shortcuts</a>.</p>
<p>In this article, we are going to not only point out some of the bad practices that result from non-semantic lists but also provide best practices for creating accessible and inclusive lists. This article focuses exclusively on unordered and ordered lists.</p>
<h2 id="unordered-list">Unordered List</h2>
<p>An unordered list is used to represent data or content whose order or hierarchy is not important. The items are commonly displayed in bullet form. A use case scenario for implementing an unordered list includes grouping related hyperlinks or a to-do list. Below is a code sample that demonstrates how to create an unordered list.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span> Shopping List <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span> Apples <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span> Pears <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span> Oranges <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="ordered-lists">Ordered Lists</h2>
<p>The ordered list is used to represent data or content whose order or hierarchy is important. The items are indexed and commonly displayed as a numbered list, in alphabet letters, or in Roman numerals. Some of the use case scenarios for implementing an ordered list include but are not limited to a food recipe guide, a list of instructions for an online registration process, or multiple-choice tests. Below is a code sample demonstrating an ordered list.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span> Online Conference Registration Process <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ol</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Go to the conference's official website and find the registration section.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Select the appropriate registration category.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Complete the online form with personal and professional details, and choose sessions if available.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Make a secure payment to confirm your registration.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Check emails for any conference updates and preparations.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ol</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="semantic-html-lists-and-accessibility">Semantic HTML lists and Accessibility</h2>
<p>Semantic HTML entails the use of HTML tags to convey the meaning of a siteās content. Regarding lists, this refers to the use of lists for structuring or grouping related content, organizing information by importance, and conveying a particular order for processes or procedures.</p>
<p>For sighted users, identifying items organized as lists, including the type of list and the total number of items, is usually straightforward. This is due to their reliance on visual formatting cues like markers and indentation. However, for people using screen readers, the experience differs significantly. Screen readers, when encountering poorly marked-up lists, may fail to announce content as list items or specify the number of items present. Instead, they announce information based on the content's code structure. For example, if paragraph tags are utilized instead of list tags, the screen reader will present the content as paragraphs. This can hinder users' understanding and expectations, as they remain unaware of the actual number of items on the perceived 'list'.</p>
<figure class="u-mb">
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ⢠Apples</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ⢠Pears</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ⢠Oranges</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<figcaption>Bad practice: <a href="https://htmhell.dev/30-bullet-list/">A paragraph disguised as a list</a></figcaption>
</figure>
<p>The use of semantic lists is crucial for people relying on screen readers. They are notified they are interacting with a list, the index of the current item (in the case of ordered lists), the number of items, and the end of a list (NVDA will announce "out of list"). For instance, in the unordered list example, the NVDA screen reader on Windows will announce the following: "List with 3 items: Bullet Oranges, Bullet Pears, and Bullet Oranges". With this announcement, the users are communicated with semantic information (a list), the number of items (3), and the prepended bullet point markers to indicate an unordered list.<br />
Having such information upfront provides people using screen readers with an understanding of what to expect from the content. This is particularly beneficial in situations where establishing appropriate expectations is key to preparing them to interact with the content, enabling them to decide whether or not to go through the list.</p>
<p>Screen readers also provide several navigation techniques that ensure people are navigating seamlessly on a webpage. This is especially important for a web page that features several lists or a large list item. They include: jumping from one list to another, jumping from one list item to another, and in some screen readers getting out of the list. These navigation techniques are aligned with the keyboard to provide shortcut keys for accessing any list content present on a webpage. This <a href="https://dequeuniversity.com/screenreaders/">resource</a> provides keyboard shortcuts to how other screen-readers interact with lists. The table below provides a summary of the navigation techniques and keyboard shortcuts for navigating lists with NVDA (NonVisual Desktop Access, version 2023.2) screen reader.</p>
<table>
<thead>
<tr>
<th>List navigation technique</th>
<th>Keyboard shortcut</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jump to next list</td>
<td>L</td>
</tr>
<tr>
<td>Jump to the previous list</td>
<td>Shift + L</td>
</tr>
<tr>
<td>Jump to next list item</td>
<td>I</td>
</tr>
<tr>
<td>Jump to the previous list item</td>
<td>Shift + I</td>
</tr>
<tr>
<td>To exit a list</td>
<td>, (comma)</td>
</tr>
</tbody>
</table>
<p>Semantic lists hugely contribute to content readability. This is especially important for people with <a href="https://www.w3.org/TR/coga-usable/#objective-2-help-users-find-what-they-need-0">cognitive and learning disabilities</a>. Large blocks of content can be difficult to read and understand. List facilitates breaking content into comprehensible chunks making it easier to read and understand the structure of the content. This is in addition to highlighting important points that might otherwise be lost when presented in a paragraph. For instance, the use of an ordered list for a food recipe demonstrates that the process has several sequential steps for its preparation.</p>
<p>In summary, lists provide the following benefits for accessibility:</p>
<ul>
<li>Improved readability</li>
<li>Highlight important points</li>
<li>Provide the total number of items in a list </li>
<li>May announce the current index of an item (in the case of ordered lists)</li>
<li>Provide shortcuts to navigate between lists</li>
<li>Provide shortcuts to navigate between list items</li>
</ul>
<h2 id="common-mistakes-and-how-to-avoid-them">Common Mistakes and how to avoid them:</h2>
<p>Here are some common mistakes associated with the implementation of semantic HTML lists.</p>
<h3 id="using-the-wrong-list-type">Using the Wrong List Type</h3>
<p>This entails implementing unordered lists when the order is important and vice versa. The key emphasis is if the order of your content or items is not relevant, implement an unordered list <code><ul></code> otherwise implement an ordered list <code><ol></code>. Below is a code sample demonstrating the relevance of using an ordered list.</p>
<p>Do not:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span> Cake Recipe <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Preheat your oven to 350°F (175°C).<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Grease and flour two 9-inch round cake pans.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>In a medium bowl, sift together the flour, baking powder, baking soda, and salt.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>Do:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span> Cake Recipe <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ol</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Preheat your oven to 350°F (175°C).<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Grease and flour two 9-inch round cake pan.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>In a medium bowl, sift together the flour, baking powder, baking soda, and salt.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ol</span><span class="token punctuation">></span></span></span></code></pre>
<p>While both unordered and ordered lists present information sequentially, their implications differ significantly, especially in languages like English which are read from left to right and top to bottom. An unordered list, despite lacking explicit indexing, still suggests a sequence. In contrast, an ordered list emphasizes a specific, often chronological, sequence. By indexing the list items, people are directed to follow the information in a particular order, which is crucial for understanding instructions or processes, as illustrated in the example of the ordered list code. This difference matters in various contexts such as instructional or procedural information, where the order of steps can affect how well they are understood and done.</p>
<h3 id="using-lists-for-non-list-content">Using Lists for Non-List Content</h3>
<p>HTML lists can sometimes be misused by authors who wish to achieve a particular visual effect on a webpage. This can involve manipulating the typical structure and presentation of lists for reasons unrelated to their intended purpose. One common example is wrapping entire paragraphs of text within <code><li></code> tags when the content is not itemized or part of a sequence. This could make the text harder to read and understand for everyone including people relying on screen readers, as it suggests a list structure where there isn't one. Instead, authors should use tags semantically, to reflect the type of content they are communicating. In this case, a paragraph <code><p></p></code> constructs are appropriate.</p>
<h3 id="creating-fake-lists">Creating āfakeā lists.</h3>
<p>This involves implementing items in a way that they are visually recognized as lists. Often, some authors use divs, spans, or paragraphs, decorating them with hyphens, emojis, images, or numbers to simulate list formatting. While this approach might seem adequate for sighted users due to the presence of markers, it falls short for people who rely on screen readers. This is because screen readers announce the content based on its code structure, not on how the lists appear visually.</p>
<p>Don't:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span> To-do List <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>*Clean<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>-Exercise <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>āMeal Prep <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Do:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span> To-do List <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Clean<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Exercise<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Meal Prep<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>In the first code sample, a screen reader will only announce the text content of the divs. People relying on screen readers will not know that this is a list or how many items are present. However, in the second example, the screen reader will announce the items as a list and provide the context of the number of items, which is three.</p>
<h3 id="implementing-invalid-list-structure">Implementing invalid list structure.</h3>
<p>Implementing invalid list structures is another common issue on the web. Using <code>divs</code>, <code>p</code>, and <code>spans</code> as direct children-ordered lists <code><ol></code> or unordered lists <code><ul></code> is not valid. According to the Web Hypertext Application Technology Working Group (WHATWG), it disallows other elements as direct children on <code><ul></code> or <code><ol></code> constructs as outlined in their <a href="https://html.spec.whatwg.org/#concept-element-content-model">HTML specifications</a>. This ensures that content is structured and presented consistently and accessible across various platforms such as web browsers and assistive technologies like screen readers, making it usable for everyone.</p>
<p>Don't:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span> My Favorite Fruits <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span> Orange<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span> Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span> Banana <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>Do:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span> My Favorite Fruits <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Orange<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span> Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span> Banana <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>Screen readers interpret and announce lists based on the code's structure. Consider the two above code samples with NVDA (screen reader). In the first code sample, a non-list child element is included within a list. This results in a confusing output. Although the screen reader recognizes three items in the list, it fails to correctly identify 'apple' as a separate item. Instead, it announces 'apple' as plain text, seemingly part of the 'Orange' item, leading to an output like 'Bullet Orange apple'. In addition, this creates confusion because the screen reader announces the presence of three items, but effectively only two are clearly announced. In the second code sample, the list is structured correctly with valid items. The screen reader accurately announces the number of items, along with each item's context, providing clarity and understanding of the list items.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Lists, when implemented semantically, are great. They facilitate the visual conveyance of relationships between items, ensuring that information is easier to consume and understand. Assistive technologies such as screen readers can also present information accurately. Let's aim to implement semantic HTML lists so that we can continue creating meaningful structures and content for everyone.</p>
<p>Thank you for reading!</p>
<h2 id="resources-for-further-reading">Resources for Further Reading</h2>
<ul>
<li><a href="https://benmyers.dev/blog/on-the-dl/">On the <code><dl></code></a> by Ben Myers</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul">The Unordered List element</a> by MDN (Mozilla Developer Network)</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol">The Ordered List element</a> by MDN</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl">The Description List element</a></li>
<li><a href="https://www.scottohara.me/blog/2018/05/26/aria-lists.html">ARIA Lists: Jump to Native ul and ol announcements</a></li>
</ul>
Swallowing camels
2023-12-16T00:00:00Z
https://htmhell.dev/adventcalendar/2023/16/
by Ida Franceen<br><blockquote>I don't like how the screen reader pronounces these numbers and I've been experimenting with different kinds of markup to get it to read better, like injecting spans to force it to make proper pausesā¦</blockquote>
<p>Reflecting on my tendency to obsess over small, but maybe not so important details has led me to write this article. I want to share my experiences and the lessons learned from past projects where I may have lost sight of what is actually important.</p>
<h2 id="the-biggest-problem">The biggest problem</h2>
<p>When I'm actively trying to improve the accessibility of a website it's easy to start with the first issue I come across. Maybe the color contrast of the icons in the footer is not good enough, making it hard for people to see them. Let's fix this!<br />
But contrast might not be the biggest and most critical issue. To identify the biggest problem we need to pinpoint the core functionality. Are there any issues there? If yes, I need to fix them first. But what is the core functionality?</p>
<p>Well, if I'm selling things then the core functionality is probably people being able to buy things. However, if I'm focused on getting a screen reader to read prices in a preferred way, and my checkout is broken due to missing form labels, it's a problem. This situation makes it hard for some people to enter their addresses and in the end unable to make a purchase. In this case, I'm definitely focusing on the wrong thing. In other words, I'm <a href="https://dictionary.cambridge.org/dictionary/english/strain-at-a-gnat-and-swallow-a-camel">straining mosquitoes from my drink while swallowing camels</a>.</p>
<h2 id="100percent-accessible">100% accessible</h2>
<p>I've always wanted to make the websites I'm working on perfect. But there is no such thing. Especially when it comes to accessibility. Websites are either more accessible or less accessible. They are never 100% accessible because we live in an evolving world where there will always be new barriers. This means that while striving for 100% accessibility is a good thing, it will remain an ongoing process of improvement rather than a fixed endpoint.</p>
<p>If my core functionality has big issues then my website is less accessible. Trying to fix it all and I will probably end up just fixing a few of the not-so-important things. If I want to move fast towards being more accessible I need to focus on fixing the core issues. There needs to be priorities.</p>
<h2 id="its-not-about-me">It's not about me</h2>
<p>As a developer, it's straightforward to have a so-called ādefinition-of-doneā where things work properly when I test it on my computer. I know there are other devices and other ways of using the web but I constantly need to remind myself of this. It is really easy to get caught up in details like how a screen reader pronounces things. But it's probably not the most important thing for me to care about.</p>
<p>Yes, of course really bad pronunciation may be an indicator that something is missing. Maybe a proper language attribute? Or maybe my screen reader is not set up properly?</p>
<p>The point is that I need to move away from my priorities and what I find annoying or interesting and <strong>consider what might be the biggest obstacle for other people</strong>.</p>
<h2 id="take-a-step-back">Take a step back</h2>
<p>So, if I find myself arguing whether an extra div makes it too tricky for people to apply custom CSS it's time to take a pause. Or if I'm spending too much time thinking about whether a description list might be better off as an unordered list then I should take a step back. I need to check the core functionality and make sure that I didn't just swallow another camel.</p>
The Ghosts of Markup Past
2023-12-15T00:00:00Z
https://htmhell.dev/adventcalendar/2023/15/
by Thomas A. Powell<br><p>As a well-seasoned web developer, a clear euphemism for my age, I reminisce about the early days of markup through the haze of strong emotional glasses. I see the past from an extreme nostalgic fondness for the simplicity of the time when a basic text editor and the ability to view source was all you needed. Yet it can just as easily go to a secret shame and embarrassment of admitting that we hacked around with invisible pixels, nested tables, and all manner of quirky presentational elements.</p>
<p>This post attempts to map those extremes and the space between them. The ultimate outcome would be to encourage you to personally explore the history of the web to unearth things that could be rediscovered, recycled, or recast for the modern web as well as learn from past mistakes. So letās get going, and I promise we will tread a path that isnāt as annoying as a scolding about the innovation of the <code><blink></code> tag.</p>
<h2 id="searching-for-markup-ghosts">Searching for Markup Ghosts</h2>
<p>Before we get started with our naughty, nice, and best never spoken again list of markup from years prior letās take a moment to address the source of these historical gems. There are basically three sources for much of HTMLās early innovation.</p>
<ol>
<li>The various proprietary introduced by browser vendors, particularly during the so-called browser wars of the late 1990s. Many of these elements eventually became part of the standard in time.</li>
<li>The W3C HTML specifications, especially those which didnāt see uptake like HTML 3</li>
<li>Use case specific markup to support the web on devices like WebTV and early smart phones</li>
</ol>
<p>Letās quickly explain each source and provide a brief commentary to temper what you find in our list of markup relics.</p>
<h3 id="spoils-and-victims-of-the-browser-wars">Spoils and Victims of the Browser Wars</h3>
<p>The <a href="https://en.wikipedia.org/wiki/Browser_wars">browser wars</a> of the late 1990s were mainly a two-way battle between Netscape and Microsoft. They eventually ended with the dominance of Internet Explorer and a period of relative stagnation of web development in the early 2000s. However, during this warring period, we experienced tremendous markup innovation, some excellent, some misguided and some a foreshadowing of the challenges we currently address.</p>
<p>Interestingly, many unusual markup elements and attributes live on from that time so you can use them in your modern browser of choice. Others will require you to find an old browser likely running on an operating system circa that time period. For those we aim to understand the mistake or idea of the markup.</p>
<h3 id="html-3-the-spec-that-would-never-be">HTML 3 - The Spec That Would Never Be</h3>
<p>Another place to mine for interesting thoughts about markup was the ill-fated <a href="https://www.w3.org/MarkUp/html3/Contents.html">HTML3 specification</a>. An interesting source for this lost specification was a book by Dave Raggett I still have in my collection. I hope you marvel how for a moment we almost had footnotes (<code><fn></code>) for the web, a collection of common <a href="https://en.wikipedia.org/wiki/Dingbat">dingbats</a>, many more semantic tags, and even relative measurement units in a few places.</p>
<p>The specification is filled with all sorts of interesting tidbit that likely deserves an article series of its own. Alas, most of these lost specification elements never found their way to browsers. You might be interested in them to have a more nuanced view of the time period or even as inspiration for what you might implement as a custom element or web component.</p>
<h3 id="early-devices-webtv-and-more">Early Devices - WebTV and More</h3>
<p>Lastly, an interesting place to find ancient innovation is to see what was implemented to support WebTV, the first broadly used web appliance. WebTV added many features that will seem familiar in intent to what we do today to support mobile devices. Interestingly, Iāll also briefly talk about the existence and influence of wireless-focused markup in WML (Wireless Markup Language).</p>
<div class="highlight u-mb">
<p><strong>Note:</strong> If you really want to experience WebTV the best route is to <a href="https://youtu.be/nOenehj8nNk?t=44">watch videos like this one on YouTube</a>.</p>
</div>
<h3 id="the-web-ancients-sometimes-had-wisdom-and-sometimes-didnt">The Web Ancients Sometimes Had Wisdom And Sometimes Didnāt</h3>
<p>As someone who worked in, taught, and wrote about web tech last century, many things have not only stood the test of time in terms of value, but have largely been reinvented in modern times out of ignorance. I may allude to such points wherever possible, but for now, I present a view of overarching themes weāll encounter in many places.</p>
<h3 id="some-best-practices-are-forever">Some Best Practices Are Forever</h3>
<p>WebTV and Wireless Markup Language (WML) exposed a constraint focused approach to web development that should ring true for best practices and performance minded developers today. For example,</p>
<blockquote>
<p>WebTV encourages that pages should be smaller than 250K</p>
</blockquote>
<p>Source: <a href="https://web.archive.org/web/20000510183823/http://developer.webtv.net/">WebTV Developerās Guide</a></p>
<p>Apparently they wanted you to consider a performance budget. The also encourage developers to use simple forms, write concise text, and test on the actual hardware! <em>Hmmmā¦apparently good advice is quite timeless!</em></p>
<h3 id="encourage-medium-restriction">Encourage Medium Restriction</h3>
<p>The constraints of device based Web at the time was quite severe. To address this WebTV both encouraged developers with advice like</p>
<blockquote>
<p>ā¦WebTV-based systems in North America and Japan (both use the NTSC television standard) display Web pages in a fixed 544 x 372 screen space. Pages may scroll vertically but not horizontally. Pages that are wider than 544 pixels will be scaled to fit that width.</p>
</blockquote>
<p>We should acknowledge that the idea of scaling pages is not dissimilar to what we do to address phones these days with appropriate <code><meta></code> viewport, relative measurements, and responsive design.</p>
<h3 id="convert-and-constraint-the-rest">Convert and Constraint the Rest</h3>
<p>Besides encouraging developers to do the right thing, many web capable devices of that time period relied upon converting proxies to convert unsupported content. For example, WebTV would transform frames into equivalent layouts using tables and transcoding images for more performant situations. These folks werenāt the first doing such things AOL had already been doing it for years when they started gatewaying to the open Internet.</p>
<p>The TV-based web wasn't the only place where device constraints and conversion thinking were employed. Early smart(er) feature phones often did cellular carrier-level conversions. You'll also see that an HTML subset/superset in strict XML was popular in the late 90s and early 2000s called WML. WMLās strictness was required because implementing a permissive and sprawling browser on the phone wasn't feasible. This markup language adopted a different, less page-oriented mindset, opting for a <code><card></code> style metaphor to allow users to move through screens. Many of these ideas would seem not too dissimilar to Google's <a href="https://en.wikipedia.org/wiki/Accelerated_Mobile_Pages">AMP</a> (Accelerated Mobile Pages), implemented nearly two decades later.</p>
<h3 id="lets-go-already">Letās Go Already</h3>
<p>So enough of the broad themes, letās get going already and I promise I wonāt throw too many random things like the fun fact that WebTV supported something called <em>JellyScript</em> which was their version of JavaScript! Sure it sounds weirdly tasty, but itās time to get to the list of arcane markup.</p>
<div class="highlight u-mb">
<p><strong>Note:</strong> Much of this content comes from my personal collection of books from the time period, including my own books like <em>HTML: The Complete Reference</em>.</p>
<p>Where possible, I try to reference information via <a href="http://archive.org/">archive.org</a>, but some are lost to time and poor search. This is not an excuse, but it reminds us of the sad state of online information preservation. Because of this, I must admit to relying on my error-prone human memory. If you, too, were present during this time and have corrections or better pointers to active resources, please add them to the comments below.</p>
</div>
<p><img src="https://htmhell.dev/images/advent2023/16_books.png" alt="Three old books about HTML" /></p>
<h3 id="my-incomplete-list-of-arcane-markup">My Incomplete List of Arcane Markup</h3>
<p><code><audioscope></code> <strong>dance party</strong> - WebTV introduced a graphical display of a current playing sound over time. Yes, that is an odd description. Maybe watch this <a href="https://youtu.be/tM7YVhar2Dg?t=33">YouTube video</a> to get the idea of something youād be hard-pressed to experience today.</p>
<p><code><au></code> <strong>for authors</strong> - This HTML 3 semantic element was used to set the page authorās name like so <code><au>Thomas A. Powell</au></code>. Not a bad element, but the name was certainly not gold, I would prefer something like <code><author></code> if we are going to revive it.</p>
<p><code><banner></code> <strong>useful and unimplemented</strong> - This was an unimplemented HTML 3 element to display a non-scrolling region at the top of a page like <code><banner>Check out our new tags!</banner></code>. Such things are done today with a <code><div></code>, other semantic block elements, or even a web component. Interestingly these and other elements from HTML3 will work in the <a href="https://lynx.invisible-island.net/">Lynx browser</a>!</p>
<p><code><bgsound></code> <strong>be gone</strong> - This Internet Explorer introduced element allowed for Midi, Wav, and other audio format files of the time to be played on a page background and even looped. Why link to a video when you can <code><bgsound src="never-gonna-give-you-up.wav" loop="infinite"></code> an unsuspecting visitor. The best part of this ancient Rick Roll is that you canāt modify the volume level or stop the playback. Youād think theyād blast that tag if only they had Devtools, which came years later with Firebug and eventually native browser tools. Today, we have <code><audio></code> if you must perform such a gag on your friends or enemies.</p>
<p><code><blackface></code> <strong>NSFW?</strong> - A WebTV addition used to set double-weight boldface. This jargon is from typographical tradition, but an article-skimming reader might worry about racial insensitivity. It's here as a warning that markup is a language, too, and as such, beliefs and norms can and do change. Itās a good thing nobody uses this platform or element anymore!</p>
<p><code><blink></code> <strong>returns?</strong> - The tag so many love to hate and one of the few relatively possibly degradable tags that got fully deprecated. Donāt worry though, if you pine for the old days just add a <a href="https://medium.com/hackernoon/web-components-from-scratch-bringing-back-the-blink-tag-783743a0589e">custom element and emulate away</a>!</p>
<p><code><body></code> <strong>attribute bonanza</strong> - Netscape, Microsoft, WebTV, and even very esoteric browsers like Oracle's proprietary browsers threw all sorts of attributes on the <code><body>,</code> from common things like setting background colors (<code>bgcolor)</code> and images (<code>background)</code>, to lesser-known attributes on WebTV to define page credits, logos, background attachment, link colors, scroll speed, margins, and more. This element always required significant revisions for each edition of my book as it was in constant flux. Most of the color and basic layout features still work, so don't be surprised if you spot some of these attributes in the wild today. (<a href="https://codepen.io/ProfPowell/pen/wvRLRJN">Demo)</a></p>
<center><code><center></code> <strong>evil and easy</strong></center>
<p>The <code><center></code> tag was a powerful tool back in the day. For modern web devs who have fought floats, auto, flex, and finally reached a simple solution with CSS grid it almost feels like we are back to where we started.</p>
<p><strong>Color attributes galore</strong> - Devs broadly chanted āGrey grey, grey go away, bring us color every day!ā when Netscape introduced color for the page on <code><body></code> with <code>bgcolor</code> and text, tables, and for the <code><font></code> tag. Understand that Netscape, not Microsoft, was initially responsible for a significant amount of the presentational ideas mixed into HTML. Don't blame them too much, the developers of the time really wanted them!</p>
<p><strong>Data binding had promise</strong> - Starting with IE4 the idea of data binding was introduced via the <code>datasrc</code> attribute to point to a data source to populate into a tag like <code><table datasrc="URL of data"></code>. For example, we might use this technology to point at a data source or an internal data island using Microsoftās <code><xml></code> tag to embed some non-rendered data in a page not too dissimilar to how <code><script src="some-other-type"></code> or <code><template></code> is used today for creating data islands. Once data was fetched, then the table rows would be dynamically rendered.</p>
<p>To implement data binding, output control is needed, so various tags such as table cells (<code><td></code>), <code><div></code>, and <code><span></code> were extended in Microsoft browsers to support a <code>datafld</code> attribute to select the field and the <code>dataformatas</code> to specify to treat the retrieved content as text or HTML.</p>
<p>Today, this form of client-side data binding is broadly implemented using JavaScript frameworks. Still, using web components, you could certainly implement a modern form of data binding even with remote REST sources. We implemented this very thing in my product <a href="https://www.zinggrid.com/docs/data/csv">ZingGrid</a>, and I have observed many people employing similar ancient IE-like data binding ideas themselves. The concept clearly was useful, just too early.</p>
<p><strong>Dingbat icons</strong> - HTML 3 had a very interesting concept to have HTML provide a standard set of dingbats that could be included in pages. Between emojis, extended Unicode entities, icon fonts and scalable SVGs we handle this quite differently today, but back then we might have had <code>&new;</code> to insert a small new icon or a clock icon with <code>&clock;</code> and so on. Interestingly many HTML 3 elements supported the dingbat attribute such as <code><ul dingbat="new"></code>.</p>
<p>A funny thing is just how time period these were as they include binhex files, diskettes, CDs, 3270 terminals, and even a Gopher server! Take a look at the figure to see some examples from the HTML 3 book.</p>
<p><img src="https://htmhell.dev/images/advent2023/16_book.png" alt="Page from an HTML book listing icons alongside their purpose and the html entity code" /></p>
<p><code><font></code> <strong>had more -</strong> A <code><font></code> tag is up there in terms of elements people frown upon, but at the time, it really was the only choice we had unless we wanted to put text in an image! The syntax isnāt that unfamiliar to many, but you might be surprised besides the <code>face,</code> <code>color,</code> and <code>size</code> attributes we saw in WebTV effects like <code>emboss,</code> <code>shadow,</code> and <code>transparency.</code> Furthermore, from Netscape 4.x and Internet Explorer 4.x we had custom font support. Albeit, it was using odd formats and wasnāt ready for use. Before we got to the custom fonts we enjoy today, things got even worse using hacks with Flash like <a href="https://mikeindustries.com/blog/sifr">SiFR.</a> Today all these approaches to typefaces on the web have been thrown in the dustbin of history where they belong.</p>
<p><strong>Footnotes almost happened</strong> - Footnotes are still missing from HTML today which is pretty odd considering the amount of academic content we put online. Obviously you can simulate such a construction, but HTML 3 provided the <code><fn></code> tag like <code><fn id="arcance_markup">The Ghosts of Markup Past</fn></code> and link to it as you might any standard target. While the element name isnāt great this probably deserves a revisitation in future HTML explorations.</p>
<p><strong>Form fields for devices</strong> - Many changes (<code><input></code>, <code><textarea></code> and more) for non-computer browsers - WebTV foreshadows how data entry on a device other than a standard computer can be difficult. The device supported a <code>showkeyboard</code> attribute to help you enter data most notably useful for <code><textarea></code> and eerily predictive of today's <code>inputmode</code> and others for assisting form fill-out on mobile. WebTV also experimented with attributes to format (<code>allcaps</code> and <code>autocaps</code>), avoid interactions (<code>autoactivate</code> and <code>autosubmit</code>) and various controls to fit the display such as color and images for fields.</p>
<p><code><frameset></code> <strong>and <code><frame></code></strong> - Sure, Jakob Nielsenās famous screed W<a href="https://www.nngroup.com/articles/why-frames-suck-most-of-the-time/"><em>hy Frames Suck (Most of the Time)</em> </a>from 1996 scared many off from this feature which was actually part of the <a href="https://www.w3.org/TR/html401/present/frames.html">W3C HTML 4 specification.</a> They still secretly work in almost every browser and when leveraged properly exhibit better performance and usability than most modern JavaScript solutions. Interestingly, a fallback with <code><noframes></code> has always been available. Do you dare explore them? I hope not.</p>
<p><code><iframe></code> - Whatās this known element doing on the list? Well, believe it or not, at first, this was the non-standard element not <<a href="https://www.w3.org/TR/html401/present/frames.html"><code>frameset></code> and <<code>frame></code> which are in the specification.</a> Initially introduced by Microsoft IE3 this element avoids deprecation because of its containment usage, but a fun arcane fact is that it wasn't officially part of a markup standard until HTML5!</p>
<p><code><img lowsrc> for slow loading images</code> - Many browser vendors implemented the <code>lowsrc</code> attribute to display an image first as the main one loads. Today we reinvent this and call them low-quality image placeholders (<a href="https://cloudinary.com/blog/low_quality_image_placeholders_lqip_explained">LQIPs</a>) executed with JavaScript and CSS. Sometimes the old ways should have lived on!</p>
<p><code><img dynscrc></code> <strong>to support animations and movies</strong> - Somehow predicting the return of animated GIFs or movies instead of images, Microsoft and WebTV supported the <code>dynsrc</code> attribute to load a dynamic asset as opposed to a static image. You could add playback buttons with the <code>controls</code> attribute. The desire to improve <code><img></code> to support videos to avoid the performance abuse of animated GIFs is an open topic today. The <code>dynsrc</code> attribute is yet another old idea that might have helped us out of our current troubles.</p>
<p><code><input type="scribble"></code> <strong>was a weird idea</strong> - HTML 3 really got innovative, if that is the appropriate word when proposing this extension to <code><input>.</code> Itās not as if that element isnāt already overloaded, but there was the idea of <code><input type="scribble" src="chart.gif" value="Annotate this chart"></code> which, as the example implies, would allow you to scribble on the image. Oddly, the <code>value</code> attribute wasnāt for instructions but rather described fallback text. The whole thing seems extremely under-specified and was never implemented, like most of the new elements introduced by HTML 3.</p>
<p><code><layer></code> <strong>might be a relief from <div> madness</div></strong> - Maybe this positioning and layering focused box element wasnāt really such a bad idea since we have just have abused <code><div></code> every since anyway. Oh and don't forget <code><ilayer></code> it's lesser known <em>relative</em> tag for inflow positioning. These elements certainly didnāt live on, but I am sure if you wanted to simulate them with a custom element you could! Please donāt, really donāt.</p>
<p><code><link></code> <strong>power unrealized</strong> - HTML 3 had common ideas like loading a stylesheet setting document relationships (next), but it also appears they had a limited inclusion format. For example, you could keep an external message in a file referenced on pages like so <code><link rel="banner" href="announcement.html"></code>. This tag touches on the need for native fragment inclusions within HTML that has yet to materialize, but I still hold out hope that someday they might.</p>
<p><strong>Lists had images and more</strong> - HTML 3 proposed the use of dingbats and images on many tags, most notably unordered lists (<code><ul></code>). The idea was you could both set an image <code><ul src="images/star.gif"></code> as well as a dingbat (<code><ul dingbat="new"></code>). The WebTV platform wasnāt nearly as aggressive with its special list features, but it added support for <code><ul type="triangle"></code>. Why you might ask? Simple, CRT monitors of the day were so low resolution that a circle and square bullet type pretty much looked the same at small size!</p>
<p><code><marquee></code> <strong>lives on</strong> - Microsoftās response to Netscapeās <code><blink></code> is actually quite involved and still supported. It is just as annoying, but for some reason doesn't have the same level of hate. There is lots you can do with a <code><marquee></code> tag like control the direction, the repeat, the speed and even use presentational attributes and nest content to <a href="https://codepen.io/ProfPowell/pen/XWoLOZY">keep the 1990s web alive in my silly demo</a> and recall <a href="https://www.htmhell.dev/adventcalendar/2022/15/">last years advent entry on <code>marquee</code> </a>as well!</p>
<p><img src="https://htmhell.dev/images/advent2023/16_marquee.gif" alt="Content moving around the screen from right to legt and top to bottom" /></p>
<p><code>maxlines</code> <strong>far ahead of its time</strong> - The <code>maxlines</code> attribute was added to tags like <code><td></code> and others to clip content on a space constrained WebTV display. It's a great example of ideas like content clipping and overflow that didn't come to CSS until much later, but interestingly were present in some corners of early web dev.</p>
<p><code><multicol></code> <strong>a bad idea then and now</strong> - Unless you kept fixed to the viewport a multiple column layout is quite a bad idea usability wise. It certainly didn't stop us from implementing <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_multicol_layout">multiple layout natively in CSS</a>! The element had basic attributes to control columns (<code>cols</code>) and spacing (<code>gutter</code>), but like most presentation HTML it was far from enough and CSS wins the day.</p>
<p><code><note></code> <strong>should have stayed because we make these anyway</strong> - One of my personal favorites in HTML 3 is the callout ideas using a <code>note</code> element. The role of the note referred to often as admonishments was set by the <code>class</code> attribute defining the role of the enclosed text. For example, <code><note class="warning">None of these tags were implemented</note></code>.</p>
<p>The written record suggests that āHTML 3 aims to provide the author with ready-made styles for purpose" so there clearly were different thoughts about <code>class</code> use than we have today. If you really must explore this Lynx does support it as you can see here as well as the previously mentioned footnote (<code>fn</code>) element.</p>
<p><img src="https://htmhell.dev/images/advent2023/16_lynx.png" alt="screenshot of the link browser" /></p>
<p><code><person></code> <strong>yet another semantic element</strong> - Another HTML 3 semantic addition this element might be used to define an index of people, maybe for a directory like so <code><person>George Washington</person></code>. Seems like this is better handled by a <code><meta></code> element or micro-data using a <code>data-*</code> attribute, but the intention seemed roughly correct. Using a <code>rel</code> attribute on <code><a></code> is another approach to personal data and is covered in a <a href="https://www.htmhell.dev/adventcalendar/2022/21/#personal-types">great entry from last year's calendar</a>.</p>
<p><code><select></code> <strong>early innovations</strong> - Making menus with <code><select></code> has been the thorn in the side of many web developers to this day. Interestingly the web ancients had thoughts. First, WebTV solved a problem we still face and solve with JavaScript via a <code>showempty</code> attribute to handle an empty select list and a default message. Second, HTML 3 even wanted to include images and shapes in menus with some odd image map like construct. It's not worth explaining something so utterly bonkers, but it does make me wonder if the <a href="https://open-ui.org/components/selectlist/">upcoming <code><selectlist></code></a> will really be any better?</p>
<p><code><sidebar></code> <strong>itās like frames, but easier</strong> - WebTV had lots of limitations and they were quite creative with how they addressed them. The <code><sidebar></code> element allowed you to pin a non-scrolling region to the left side of the screen. Obviously useful for navigation bars and the like, it was in some sense a way to have a frame like experience when they couldnāt support frames.</p>
<p><code><shadow></code> <strong>emerges</strong> - Before CSS introduced them, you could make a text shadow at least on WebTV without using an image. This <code><shadow>drop</shadow></code> example would drop a small black shadow down and to the right on that platform. Clearly something that doesn't belong in markup, but a fun lesser known example of ancient presentational HTML.</p>
<p><code><spacer></code> <strong>made layout hacks easy</strong> - When <a href="https://archive.org/details/creatingkiller1997sieg/mode/2up">Creating Killer Web Sites</a> why add an invisible pixel to your page? Netscape 4 innovates a tag <code><spacer type="block" width="100" width="100"></code> that does just the same!</p>
<p><code><tab></code> <strong>or spaces please no for HTML</strong> - This element will make your presentation mixed into semantic markup warning meter go to 11. HTML 3 proposed that we have <code><tab>,</code> <code><tabstop></code> and even <code><tab to t8></code> which was some weird attribute without an assignment to get to a particular tab level. It even had an <code>align</code> attribute to tab from different directions. I acknowledge that layout on the web was hard in the early days, but revisiting this "solutionā was even was probably the worst part of my markup historical tour.</p>
<p><code><table></code> <strong>Layout Simplicity?</strong> - Many modern web developers will clown on the idea of using tables stacked or nested as some ancient way as they nest <code><div></code> tags like fiends. Interestingly it was quite possible using a combination of relative units in percentage and fixed values to provide variable flexible layouts. Add in some colors and rudimentary alignment with <code><center></code> and more and away you go. Try the <a href="https://codepen.io/ProfPowell/pen/wvRLRJN">demo</a> and resize it. I used upper case and more to have some real 1990s fun that still works in your browser.</p>
<p><img src="https://htmhell.dev/images/advent2023/16_table.gif" alt="a flexible table layout" /></p>
<p><strong>Transitions</strong> - Both WebTV and Microsoft Internet Explorer had some interesting thoughts about inter-page transitions. While Microsoft used a very strange ActiveX powered mechanism for filtering and transition effects, WebTVās syntax was a simple as <code><a href="page" transition="slideleft">Load Page</a></code>. If you squint hard, you'll see the emerging MPA transitions ideas from the <a href="https://developer.chrome.com/docs/web-platform/view-transitions/">View Transitions API</a> being played with almost 25 years ago!</p>
<p><strong>VRML</strong> - To round out this list, let us reminisce about <a href="https://en.wikipedia.org/wiki/VRML">VRML</a>(Virtual Reality Markup Language). Being that I am associated with UC San Diego, which is significantly responsible for this technology, I can bear some sliver of responsibility for this technology. I only conclude our list with this technology as it seems appropriate with the <a href="https://github.com/immersive-web/model-element/blob/main/explainer.md">recent introduction of <code><model></code></a> and likely many more ideas like WebXR that will become real with the arrival of the Apple Vision Pro. The past really did predict the future, albeit at a much lower rez!</p>
<p><img src="https://htmhell.dev/images/advent2023/16_vrml.png" alt="a flexible table layout" /></p>
<h2 id="back-to-the-markup-future?">Back to the Markup Future?</h2>
<p>Thereās a lot more I could have revealed in this historical markup tour including hacking with expected HTML bugs documented in my book as well as others, various interesting HTML 3 ideas with checksums, list control, and even incredibly esoteric browsers with their special elements. The point of this list was to expose enough of the ancient ideas to help us get ready for the future.</p>
<p>As a language, HTML really isnāt done as languages rarely finalize. However, itās been stagnating for some time. Looking at the new things landing in the last year or two, clearly, this is no longer the case, and it is nearly certain we will see many new markup elements this decade. Some are in the pipeline due to new devices, emerging web access modes such as AR and VR, and the desire to standardize UI elements via (<a href="https://open-ui.org/">open-ui.org</a>). However, if market competition heats up due to browser-related regulatory action, you can almost guarantee we will see a back-to-the-future moment in web technology with all sorts of new features introduced as browser vendors try to capture or retain market position. Hopefully, these arcane blasts remind you that this has all happened before, and it will happen again and again and againā¦</p>
Getting started with Web Performance š
2023-12-14T00:00:00Z
https://htmhell.dev/adventcalendar/2023/14/
by Alistair Shepherd<br><p>
</p><figure>
<a href="https://htmhell.dev/images/advent2023/web-performance/www-sheep.jpg">
<picture>
<source type="image/avif" srcset="https://htmhell.dev/images/advent2023/web-performance/www-sheep-740.avif 740w, https://htmhell.dev/images/advent2023/web-performance/www-sheep-1000.avif 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" />
<source type="image/webp" srcset="https://htmhell.dev/images/advent2023/web-performance/www-sheep-740.webp 740w, https://htmhell.dev/images/advent2023/web-performance/www-sheep-1000.webp 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" />
<img alt="Illustration of a sheep eating grass with 'www.'' written on its side. Someone is observing it at a distance through binoculars" src="https://htmhell.dev/images/advent2023/web-performance/www-sheep-740.jpg" srcset="https://htmhell.dev/images/advent2023/web-performance/www-sheep-740.jpg 740w, https://htmhell.dev/images/advent2023/web-performance/www-sheep-1000.jpg 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" width="1000" height="697" loading="eager" fetchpriority="high" />
</picture>
</a>
<figcaption>Carefully observing websites in the wild</figcaption>
</figure>
<p></p>
<p>As the murderous tortoises start to converge on RyÅ«jiās hideout, they pull out their phone. Itās a cheap, older device but itās survived the toils of the tortoise-ageddon well so far. Thankfully the internet still exists, although a bit slower, so theyāre able to search online for how to scare tortoises away. The first result looks promising so they quickly tap through to find the information they need. But as the loading bar moves at barely a crawl, RyÅ«ji realises with horror that this website is loading far too slowlyā20 seconds have passed and itās still just a white screen! They look up and realise theyāve run out of time, poor website performance was RyÅ«jiās downfall.</p>
<p>As RyÅ«ji discovered, web performance really matters, and is about making websites fast. I hope that most of us donāt encounter as drastic circumstances as they have! For the rest of us a well performing website is more accessible to people, offers a more enjoyable experience, and may even encourage more views, shares or sales.</p>
<p>But web performance is also quite tricky! Let us go through some of the reasoning, jargon, metrics, tools, and some top tips on how you can improve the performance of websites you look after. Whether youāre a practised performance prophet, dipping your toes into the river of load times, or are desperately hoping someone will explain what an FCP is and why Google wants one, letās all dig into web performance together!</p>
<p>Weāll be diving into the river of load times and exploring what web performance is, why itās important, how to measure it and finally my click-baity ā<strong>Ten Wild Web Performance Tips! Youāll be saving number 5 for later!</strong>ā. If you already know your CLSā from your FCPs, lab from field data, and are well familiar with Lighthouses (not the ones with big lights) then you can <a href="https://htmhell.dev/adventcalendar/2023/14/#tips">jump straight to the tips</a>.</p>
<p>Before we start getting into it, thereās one thing I want to make super clear especially if you havenāt spent a lot of time with web performance: Web performance can be pretty tricky. I mention this so you donāt get demoralised if you hit a wall or struggle to identify an issue as weāre going to fill your performance toolbox with some great metrics, strategies, tools, and tips. These will help you make some real improvements, but know that even with years of experience some things are still a challenge to find! With our toolbox youāll find that developers, designers, and even managers can each make a meaningful impact on website performance without necessarily being crowned Speed Supremo.</p>
<h2 id="table-of-contents">Table of Contents</h2>
<ol>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#why">Why web performance is important, why should we care?</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#look">What does web performance look like?</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#metrics">Performance science with Metrics</a>
<ol>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#cwv-crux">Core Web Vitals and the Chrome User Experience report</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#lcp">LCP ā Largest Contentful Paint</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#inp">INP ā Interaction to Next Paint</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#cls">CLS ā Cumulative Layout Shift</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#bonus-metrics">Some bonus metrics if youāre keen</a></li>
</ol>
</li>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#testing">Testing 101</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#tips">Ten Wild Web Performance Tips!</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/14/#what-next">What next?</a></li>
</ol>
<h2 id="why">Why web performance is important, why should we care?</h2>
<p>Web performance can often be overlooked, and thatās generally a big mistake. Itās easy to put it on the backlog, assume your development tooling is solving it for you, or think that modern hardware and networks remove the need completely.</p>
<p>But performance is just as important as it ever has been, perhaps even more so! More people than ever are online, relying on the web for crucial services, yet the gap in hardware between the best iPhone and cheapest smartphone has never been more extreme. For that reason, web performance isnāt just a tech problem to solve, itās a social issue. Our societies require people to use websites, yet we serve 10MB poorly-optimised JavaScript-heavy āappsā via metered connections to a $30 smartphoneāthatās never going to work!</p>
<p>Phew, I got a bit serious there! To reiterate, building a website that performs well is important not just for SEO and sales, but also to open the web to more people making it more inclusive and accessible to all.</p>
<p>On a personal level, I travel on trains quite a bit and thereās nothing like free train WiFi or spotty 3G or 4G in rural areas for showing up websites with poor performance (at least in the UK!). When one site takes 5 seconds to load and another takes 50 seconds, I know which one Iād prefer to use.</p>
<p>If you need more convincing, web performance can often be directly correlated to business metrics like traffic, conversions, and sales. <a href="https://wpostats.com/">WPO Stats</a> lists fantastic performance-related Case Studies if you need material to send to your boss or client. Google is also using web performance as a ranking factor in its algorithmsāIāve seen performance improvements increase impressions from Google Search by 300%. Thatās nothing to scoff at!</p>
<h2 id="look">What does web performance look like?</h2>
<p>Simply put, web performance is how fast a website loads and feels, and how fast it feels while using it.</p>
<p>There are a few different factors that weāll get into, but a website that takes a second to go from clicking the link to a fully complete page is pretty quick. On the other hand a slow page might have a long loading screen, parts missing for ages after load, or take a second to react to every click.</p>
<video style="aspect-ratio:740/208" width="740" height="208" controls="" muted="" playsinline="" preload="metadata">
<source src="https://htmhell.dev/images/advent2023/web-performance/general-740.mp4" type="video/mp4" />
</video>
<details>
<summary>Video description</summary>
<p>The video shows two websites loading side-by-side, with a timer below each of them. The page they're loading is visually identical at the end, but they load differently.</p>
<p>The page itself features the HTMHell logo, a title of "HTMHell's Handsome Hat House", a couple of 'article' images, some placeholder content and a large image of hats filling half of the page.</p>
<p><strong>On the left</strong> the page loads in with unstyled text after 1 second. As images slowly load in top-to-bottom, they move the other page content around. After 13 seconds the headings increase in size and change style, which after a further 2 seconds is replaced with a custom font. The page finishes loading at 15 seconds.</p>
<p><strong>On the right</strong> the page loads in a lot faster, and without any images moving content around. The images and font load in significantly earlier, and the page has finished loading at 4.8 seconds.</p>
</details>
<p><a href="https://htmhell.dev/images/advent2023/web-performance/general.mp4">Full size video</a></p>
<h2 id="metrics">Performance science with Metrics</h2>
<p>Now we know what web performance feels like! Unfortunately feeling is notoriously a not great⢠way of comparing things, and humans unfortunately havenāt evolved the ability to instinctively determine web performance just yet. I have been trying to hone my sense of Time To First Byte but am yet to see positive results.</p>
<p>Thereās some good news though, the web performance and development community have come up with some fantastic tests, metrics, and analysis methods to help us measure, understand, and compare performance. These make it easy to quantify improvements, set measurable goals and find out where websites can be improved.</p>
<p>I am back with the bad news though: thereās bloody loads of them. Theyāre a mixture of meaningless jargon and 3 or 4 letter initialisms that will leave you extremely confused and wondering if youāre reading a sci-fi novel. Just take a look at some of this mess:</p>
<p>
<a href="https://htmhell.dev/images/advent2023/web-performance/wordcloud.png">
<img alt="Wordcloud with around 50 web-performance related terms. The terms donāt matter, the point is how ridiculously busy and chaotic it is" src="https://htmhell.dev/images/advent2023/web-performance/wordcloud-740.png" srcset="https://htmhell.dev/images/advent2023/web-performance/wordcloud-740.png 740w, https://htmhell.dev/images/advent2023/web-performance/wordcloud-1000.png 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" width="1000" height="500" loading="lazy" />
</a>
</p>
<p>If you know your web perf and questioned PES and THWIMP, pat yourself on the back. Those ones arenāt real and Iām pulling your leg. PES is obviously Parakeets Eat Seeds and a THWIMP is obviously this cutie from Super Mario World:</p>
<p>
<img style="image-rendering:pixelated" alt="Thwimp from Super Mario World, pixel graphics big rock with spiky edges and a grumpy frown face" src="https://htmhell.dev/images/advent2023/web-performance/thwimp.png" width="64" height="64" loading="lazy" />
</p>
<p>In all seriousness however, there are loads of metrics, definitions, and jargon that are so easy to forget or get confused about. Iām going to simplify this down, and give you a glossary and description of only the metrics you really need to know. For the moment this will be a summary, weāll get into the tips on how to fix them later!</p>
<h3 id="cwv-crux">Core Web Vitals and the Chrome User Experience report</h3>
<p>If youāve used Googleās <a href="https://pagespeed.web.dev/">Pagespeed Insights</a> or <a href="https://htmhell.dev/adventcalendar/2023/14/search.google.com/search-console/">Search Console</a> then you may have already heard of Core Web Vitals or the Chrome User Experience report.</p>
<p>Core Web Vitals, initialised to CWV, is a collection of metrics that help to measure web performance in a way that emulates how a user may feel about performance. Google collects and analyses these metrics and a few others to know how fast a website performs. These results can then be used in their search algorithms to reward well-performing websites.</p>
<p>How do they collect those metrics? People navigating with Google Chrome have the performance of the websites they visit anonymously recorded and sent to Google. Thatās where the Chrome User Experience Report (CRUX) comes in. CRUX consists of a giant database of the real-world performance of all websites that are visited by a statistically significant number of Chrome users.</p>
<p>You can access <a href="https://developer.chrome.com/docs/crux/">CRUX</a> data through <a href="https://pagespeed.web.dev/">Pagespeed Insights</a>, <a href="https://htmhell.dev/adventcalendar/2023/14/search.google.com/search-console/">Google Search Console</a>, <a href="https://developer.chrome.com/docs/crux/history-api/">Google developer APIs</a>, and using various other tools. You can also <a href="https://developer.chrome.com/docs/crux/bigquery/">query the database itself using Googleās BigQuery</a>, but watch out as that can get expensive quickly!</p>
<p>The three metrics used in Core Web Vitals are some of the best we have to understand how fast a website is for real people, and so those metrics will be what we focus on. You can find lots more information about these metrics and many more at <a href="https://web.dev/explore/learn-core-web-vitals">web.dev</a>, Iād recommend checking that out for more technical info.</p>
<h3 id="lcp">LCP ā Largest Contentful Paint</h3>
<p>āWow this website is taking a long time to loadā we may say, as we click on a link and are left with a blank or sparse screen for what feels like an age. What weāre seeing here is a slow Largest Contentful Paint.</p>
<video style="aspect-ratio:740/208" width="740" height="208" controls="" muted="" playsinline="" preload="metadata">
<source src="https://htmhell.dev/images/advent2023/web-performance/lcp-740.mp4" type="video/mp4" />
</video>
<details>
<summary>Video description</summary>
The video shows two pages loading side-by-side, with a timer below each of them. The page they're loading is visually identical at the end, but they load differently.
The page itself features the HTMHell logo, a title of "HTMHell Horticultural Heuristics", some placeholder content and a background image of some house plants.
Both pages load the logo, title and content similarly within 3.5 seconds. The page on the right loads a background image in relatively quickly, appearing all at once at 4 seconds in. The page on the left takes significantly longer to load the image, doing it from top-to-bottom and taking 15.7 seconds before it's completed.
</details>
<p><a href="https://htmhell.dev/images/advent2023/web-performance/lcp.mp4">Full size video</a></p>
<p>LCP measures how long it takes for the largest single piece of content to appear when loading the page. That piece of content could be an image, heading, video, or graphic. For most websites it ends up being a large āheroā image at the top of the page, although this varies per-site.</p>
<p>The goal of LCP is to understand when the main parts of the page loadāthe parts that the visitor will be most interested in. Your goal should be to load those elements as fast as possible. <a href="https://web.dev/articles/defining-core-web-vitals-thresholds#achievability">Aim for an LCP of 2.5 seconds or less</a>.</p>
<h3 id="inp">INP ā Interaction to Next Paint</h3>
<p>Imagine weāre browsing a website and click a button or link. We expect it to react instantly but instead weāre left waiting. Did it work? Maybe it didnāt go through? Should we click it again? Turns out it was just very slow at reacting to clicks. Weāve encountered poor Interaction to Next Paint in the wild.</p>
<video style="aspect-ratio:740/352" width="740" height="352" controls="" muted="" playsinline="" preload="metadata">
<source src="https://htmhell.dev/images/advent2023/web-performance/inp-740.mp4" type="video/mp4" />
</video>
<details>
<summary>Video description</summary>
<p>The video shows a page with HTMHell logo, a title of "HTMHell Humour Hobbies" and a set of accordions.</p>
<p>The recording shows a user trying to interact with the accordion buttons, but it takes a second or two after clicking before the accordion responds, making it a bit confusing as they seemingly randomly open/close.</p>
<p>The accordion titles are a bit silly:</p>
<ul>
<li>What are the HTMHell Humour Hobbies?</li>
<li>Is this entire thing complete nonsense?</li>
<li>Am I good at coming up with placeholder content for demos?</li>
<li>No it appears that I'm probably not actually</li>
</ul>
</details>
<p><a href="https://htmhell.dev/images/advent2023/web-performance/inp.mp4">Full size video</a></p>
<p>How a website appears is a major part of performance, but if everything appears quickly yet takes a second for any of the buttons or links to respond thatās pretty frustrating! Interaction to Next Paint measures the time it takes between the user interacting with a page and the website something on the screen in response.</p>
<p>Websites with lots of JavaScript running in the background or heavy tasks (like network requests) running on clicks may take longer to āreactā to a button press. Your goal should be to make sure interactions are as snappy and responsive as you can. Ideally this will be as fast as you can make it, but <a href="https://web.dev/articles/inp#what_is_a_good_inp_score">you should aim for at least within 200ms</a>.</p>
<p><em>Note: Interaction to Next Paint is not yet in Core Web Vitalsā<a href="https://web.dev/blog/inp-cwv">itāll replace the similar First Input Delay (FID) in March 2024</a>. I would recommend focusing on INP already though, youāll be getting ready and very likely improving FID at the same time!</em></p>
<h3 id="cls">CLS ā Cumulative Layout Shift</h3>
<p>Weāre buying a new Microwave online, have just gone through the full checkout process and are about to click confirm. Oh silly us, weāve accidentally added 10 Microwaves! We go to click the Cancel button, but suddenly an advert appears, shifting the āConfirmā button to where we clicked! Disaster! Thatās Cumulative Layout Shift, one of the trickiest and most frustrating performance metrics.</p>
<video style="aspect-ratio:740/494" width="740" height="494" controls="" muted="" playsinline="" preload="metadata">
<source src="https://htmhell.dev/images/advent2023/web-performance/cls-740.mp4" type="video/mp4" />
</video>
<details>
<summary>Video description</summary>
<p>The video shows a page with HTMHell logo, a title of "HTMHell Hamburger Heating Hardware", and a product page for a microwave. There's an image of a generic microwave, a title of "Microwave", description "A very good microwave I'm sure" and "Selected quantity Ć10". Below are two buttons, one saying "Pay now ā $6,000" and another saying "I don't need that many microwaves".</p>
<p>The recording shows a mouse cursor reviewing the page and highlighting the quantity line. They then go and hover over the "I don't need that many microwaves", but just before they click an advert appears above the two buttons. The advert says "YOU NEED MORE BARBECUES BUY BARBECUES AD" and shifts the two buttons down so the user has instead accidentally clicked on the "Pay now ā $6,000" button. It changes to "Loading...", the user highlights things in frustration and then the buttons and ad are replaced with a message saying "Success! Enjoy your microwaves".</p>
</details>
<p><a href="https://htmhell.dev/images/advent2023/web-performance/cls.mp4">Full size video</a></p>
<p>Cumulative Layout Shift measures the amount the page shifts around in an unexpected way. Our Microwave example is particularly drastic, where someone ends up doing something they didnāt want to do based on the page shifting. In addition to interactive elements shifting under someoneās cursor, CLS can create extremely frustrating reading experiences where sentences jump around whilst youāre reading them.</p>
<p>CLS is trickier to quantify than LCP and FID. Throughout the userās visit, every content shift will be given a score based on how much it moved and added up for a ācumulativeā total. Lower is better here, so a single small shift may be 0.04 units, whilst a huge advertisement loading late, and pushing content out of the way would be higher at 0.5. <a href="https://web.dev/articles/defining-core-web-vitals-thresholds#achievability_3">You should aim for absolutely no CLS, but less than 0.1 at least</a>.</p>
<h3 id="bonus-metrics">Some bonus metrics if youāre keen</h3>
<p>There are plenty more metrics, and a few others that are very handy to know!</p>
<p>
</p><details>
<summary>Bonus Metrics: FCP, TTFB, SI and FID</summary>
<p><strong>First Contentful Paint</strong> (<a href="https://web.dev/articles/fcp">FCP</a>) is similar to Largest Contentful Paint (LCP), but whilst LCP measures the largest piece of content rendered on the screen, FCP measures the first content shown. I guess it <em>is</em> in the names.</p>
<p>Your FCP could be triggered just by a small amount of text, leaving the page still looking mostly incomplete until the titles, graphics and images appear after. It doesnāt really suggest how fast the site feels, but it is helpful to understand how quickly <em>something</em> is shown, even if this is not the main content of the page.</p>
<p><strong>Time to First Byte</strong> (<a href="https://web.dev/articles/ttfb">TTFB</a>) is a very simple metric: How long does it take from the very beginning of the request to zoom off to the server and come back with data? TTFB doesnāt tell you anything about what that data is and how long it will take to fully load or appear on the screen, but it can give you some insight into how fast or far away your server is.</p>
<p><strong>Speed Index</strong> (<a href="https://developer.chrome.com/docs/lighthouse/performance/speed-index/">SI</a>) is a really handy general metric that provides a rough idea of performance, but doesnāt really tell you any specific information. It measures how quickly a page is visually complete above-the-fold (what is visible on first visit without scrolling). Itās a bit older but still handy for comparison and ongoing tracking.</p>
<p><strong>First Input Delay</strong> (<a href="https://web.dev/articles/fid">FID</a>) is similar to Interaction to Next Paint, but uses a slightly different measurement method and only considers the first input. Interaction to Next Paint is likely more useful especially once adopted by CWV but FID can give you a good impression of responsiveness.</p>
</details>
<p></p>
<h2 id="testing">Testing 101</h2>
<p>There are lots of fantastic tools and services that help you to measure and check the performance of websites. Each provides you with access to at least some of the metrics weāve discussed above, and many will also make it easy to see where improvements can be made. These vary in depth, functionality, and difficulty so try them out and see what works best for you! Most of these tools are free, but there are some bonus paid ones.</p>
<p>One distinction to keep in mind is that not all kinds of tests are the same when it comes to web performance. There are two main types, lab tests and field tests.</p>
<ul>
<li><strong>Lab tests</strong> are run in a controlled environment, like a laboratory! They go off and do some experimentation on your website and report the result. This might be run on your computer or on a remote server. Theyāre very good at giving you controlled, somewhat consistent results with lots of debug information and suggestions.</li>
<li><strong>Field tests</strong> are real-world testing, including the Core Web Vitals we mentioned previously. The data they report comes from real users and is a better representation of how your website performs āin the wildā. Theyāre best for knowing how your website performs on real devices, on real networks and for your audience.</li>
</ul>
<p><a href="https://pagespeed.web.dev/">Googleās Page Speed Insights</a> is a handy tool that combines running a lab test with Core Web Vitals to gain some simple field test data. By entering a URL, youāll get average scores across the Core Web Vitals, alongside a few other metrics from CRUX. It will also run a lab test from a Google server giving you those metrics along with some more in-depth info and suggestions. Page Speed Insights is a fantastic place to start with performance, as it makes it really easy to get the information you need and some tips on what you can improve. Donāt take the improvement times as gospel though, you will likely see some improvement but unlikely as much as it says!</p>
<p>
</p><figure>
<a href="https://htmhell.dev/images/advent2023/web-performance/pagespeed-insights.png">
<picture>
<source type="image/avif" srcset="https://htmhell.dev/images/advent2023/web-performance/pagespeed-insights-740.avif 740w, https://htmhell.dev/images/advent2023/web-performance/pagespeed-insights-1000.avif 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" />
<source type="image/webp" srcset="https://htmhell.dev/images/advent2023/web-performance/pagespeed-insights-740.webp 740w, https://htmhell.dev/images/advent2023/web-performance/pagespeed-insights-1000.webp 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" />
<img alt="screenshot of a PageSpeed Insights result for developer.mozilla.org with URL field, āAnalyzeā button and tabs for āMobileā and āDesktopā. It reports āDiscover what your real users are experiencing. Core Web Vitals Assessment: Passedā. It gives results for LCP, FID, CLS, FCP, INP, TTFB and some metadata. Cut off at the bottom is a heading āDiagnose performance issuesā" src="https://htmhell.dev/images/advent2023/web-performance/pagespeed-insights-740.jpg" srcset="https://htmhell.dev/images/advent2023/web-performance/pagespeed-insights-740.jpg 740w, https://htmhell.dev/images/advent2023/web-performance/pagespeed-insights-1000.jpg 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" width="1000" height="586" loading="lazy" />
</picture>
</a>
<figcaption>Screenshot of a PageSpeed Insights result for developer.mozilla.org</figcaption>
</figure>
<p></p>
<p><a href="https://developer.chrome.com/docs/lighthouse/overview/">Lighthouse</a> is a lab test that is built into Google Chrome and most Chromium-based browsers. Itās actually what powers the Page Speed Insights lab test and gives you the same information, but because this runs from your computer you have a little more control over the test conditions. You can access it by opening the browser developer tools with F12 and going to the āLighthouseā or āAuditā tab. Itās a very easy and handy way to quickly check a page youāre working on.</p>
<p>
</p><figure>
<a href="https://htmhell.dev/images/advent2023/web-performance/lighthouse.png">
<img alt="screenshot of Lighthouse within Google Chrome dev tools. Itās testing the HTMHell homepage and has achieved 100 scores across Performance, Accessibility, Best Practices and SEO, and PWA has confetti on it. Under Performance it gives statistics for FCP, LCP, Total Blocking Time, CLS and Speed Index" src="https://htmhell.dev/images/advent2023/web-performance/lighthouse-740.png" srcset="https://htmhell.dev/images/advent2023/web-performance/lighthouse-740.png 740w, https://htmhell.dev/images/advent2023/web-performance/lighthouse-1000.png 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" width="1999" height="1160" loading="lazy" />
</a>
<figcaption>Screenshot of a Lighthouse report for htmhell.dev</figcaption>
</figure>
<p></p>
<p>I really like <a href="https://gtmetrix.com/">GTMetrix</a> for performance tests also, and similar to Page Speed Insights and Lighthouse itās extremely easy to use and gives you direct feedback and suggestions you can look into. Itās based on a lab test, reports all of the common metrics and has suggestions on improvements that are helpfully categorised by āImpactā which makes prioritising improvements a bit easier.</p>
<p>
</p><figure>
<a href="https://htmhell.dev/images/advent2023/web-performance/gtmetrix.png">
<picture>
<source type="image/avif" srcset="https://htmhell.dev/images/advent2023/web-performance/gtmetrix-740.avif 740w, https://htmhell.dev/images/advent2023/web-performance/gtmetrix-1000.avif 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" />
<source type="image/webp" srcset="https://htmhell.dev/images/advent2023/web-performance/gtmetrix-740.webp 740w, https://htmhell.dev/images/advent2023/web-performance/gtmetrix-1000.webp 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" />
<img alt="screenshot of GTMetrix performance report of htmhell.dev. It includes report metadata, a grading of A and performance and structure grades of 100%, the Web Vitals LCP, TBT and CLS. Below are tabs for more information including āSummaryā, āPerformanceā, āStructureā, āWaterfallā, āVideoā and āHistoryā." src="https://htmhell.dev/images/advent2023/web-performance/gtmetrix-740.jpg" srcset="https://htmhell.dev/images/advent2023/web-performance/gtmetrix-740.jpg 740w, https://htmhell.dev/images/advent2023/web-performance/gtmetrix-1000.jpg 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" width="1000" height="586" loading="lazy" />
</picture>
</a>
<figcaption>Screenshot of GTMetrix performance report for htmhell.dev</figcaption>
</figure>
<p></p>
<p><a href="https://www.webpagetest.org/">WebPageTest</a> is amazing, and is where we start to get into the more technical and powerful tools for performance testing. It runs a lab test that is extremely configurable and returns a huge amount of data that you can then analyse. Although it does give you some suggestions, its real power is exposing data about the full loading cycle to you in a way that you can really dig into. For example, did a specific third-party script delay the loading of other assets? When you need to really analyse a website in a repeatable way with as much data as possible WebPageTest is ideal.</p>
<p>
</p><figure>
<a href="https://htmhell.dev/images/advent2023/web-performance/webpagetest.png">
<picture>
<source type="image/avif" srcset="https://htmhell.dev/images/advent2023/web-performance/webpagetest-740.avif 740w, https://htmhell.dev/images/advent2023/web-performance/webpagetest-1000.avif 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" />
<source type="image/webp" srcset="https://htmhell.dev/images/advent2023/web-performance/webpagetest-740.webp 740w, https://htmhell.dev/images/advent2023/web-performance/webpagetest-1000.webp 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" />
<img alt="screenshot of webpagetest result screen. It includes a lot of metrics but is broken down into three sections, āPerformance Summaryā, āPage Performance Metricsā and āVisual Page Loading Processā. The Performance Summary has blocks for āIs it Quick?ā, āIs it Usable?ā and āIs it Resilient?ā. The Page Performance Metrics lists a lot of metrics, and the Visual Page Loading Process shows page screenshots against the time they were taken." src="https://htmhell.dev/images/advent2023/web-performance/webpagetest-740.jpg" srcset="https://htmhell.dev/images/advent2023/web-performance/webpagetest-740.jpg 740w, https://htmhell.dev/images/advent2023/web-performance/webpagetest-1000.jpg 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" width="1000" height="586" loading="lazy" />
</picture>
</a>
<figcaption>Screenshot of a very small part of the WebPageTest report for htmhell.dev. There is much, much more and a lot more depth that the summary you see here</figcaption>
</figure>
<p></p>
<p>In addition to Lighthouse, Chromium-based browsers have some very handy performance tools built into the development tools. The <strong>Network pane</strong> is handy for seeing the full list of requests made by the browser, where they came from and how long they took. This is ideal for checking request order, caching behaviour, and what third-parties are being connected to. The <strong>Performance pane</strong> gives you even more information about how the browser is performing, including JavaScript call stacks, frame counts, tasks in the event loop, and more. These are pretty advanced tools but very powerful.</p>
<p>
</p><figure>
<a href="https://htmhell.dev/images/advent2023/web-performance/chrome-performance.png">
<picture>
<source type="image/avif" srcset="https://htmhell.dev/images/advent2023/web-performance/chrome-performance-740.avif 740w, https://htmhell.dev/images/advent2023/web-performance/chrome-performance-1000.avif 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" />
<img alt="screenshot of Performance pane within Chrome Devtools. Thereās a lot going on including a filmstrip of the site loading, Network Graph and collapsed sections for Timings, Main, GPU, and more. At the bottom there is a Summary and tabs for Bottom-Up, Call Tree and Event Log" src="https://htmhell.dev/images/advent2023/web-performance/chrome-performance-740.jpg" srcset="https://htmhell.dev/images/advent2023/web-performance/chrome-performance-740.jpg 740w, https://htmhell.dev/images/advent2023/web-performance/chrome-performance-1000.jpg 1000w" sizes="(min-width: 62.5rem) 46.25rem, (min-width: 768px) calc(100vw - 16.25rem), calc(100vw - 2.5rem)" width="1000" height="586" loading="lazy" />
</picture>
</a>
<figcaption>Screenshot of the Performance tab within the Chrome developer tools. The first things you would look at here are Network, Timings, Frames and Main</figcaption>
</figure>
<p></p>
<p><strong>Firefox and Safari</strong> also have handy performance tools built-in. Safari has the Timelines tab which records a page load and provides screenshots and a huge amount of detail on Network Requests, Layout & Rendering, JavaScript & Events, and CPU usage. Firefoxās Network tab works similarly to Chromeās with the addition of a quick āPerformance Analysisā which reports details about content types, weights, and caching.</p>
<p>Weāve discussed the Chrome User Experience Report a few times already, and a couple of methods of getting that data. One way you can access that data, particularly when looking at how average measurements have changed over time, is by using the <strong>CRUX History API</strong>. There are a few tools for accessing this, but my favourite is through <a href="https://colab.research.google.com/github/GoogleChrome/CrUX/blob/main/colab/crux-history-api.ipynb">the CRUX History API notebook on Google Colab</a>. By inserting a URL and a free CRUX API key youāll be able to see graphs of how CRUX metrics have changed over time.</p>
<p>Those are the main free tools which I use regularly for testing, but there are a few others that may cost money or are a bit more specific:</p>
<ul>
<li><a href="https://calibreapp.com/">Calibre</a> ā automated lab testing and monitoring</li>
<li><a href="https://www.speedcurve.com/">Speedcurve</a> ā automated lab and field testing, and monitoring</li>
<li><a href="https://github.com/zachleat/speedlify">Speedlify</a> ā open-source tester and aggregator of web performance and accessibility</li>
<li><a href="https://treo.sh/">Treo</a> ā automated lab testing and core web vitals analysis</li>
<li>Open-source tools based on Lighthouse ā there are a ton of open-source tools that use lighthouse for whole-site crawling and testing and more</li>
</ul>
<h2 id="tips">Ten Wild Web Performance Tips! Youāll be saving number 5 for later!</h2>
<p>Okay, now itās time for the section weāve all been waiting for, unless you skipped here from the links in the intro. If you did, you missed some real great stuff up there and at least three jokes that I should have never committed to writing.</p>
<p>On these specific tips, they are some of the most common issues and fixes for websites I encounter. Not every site has all of them, but generally at least a handful. Going into the exact code and details for each will put everyone to sleep, but theyāll give you somewhere to start looking!</p>
<h3 id="1-test!-test!-test!">1. Test! Test! Test!</h3>
<p>Tools like the ones above can make it easy to see a simple summary of web performance and give you tips on what to focus on. Run some lab tests after major changes to confirm youāve actually improved performance, or to check you havenāt caused a regression.</p>
<p>Also consider setting up automated performance monitoring to monitor performance over time, alert you to regressions, and to flag issues on specific pages before lots of visitors are affected.</p>
<h3 id="2-optimise-images-optimise-them-again-and-then-once-more-for-good-luck">2. Optimise images, optimise them again, and then once more for good luck</h3>
<p>Unless you already have perfectly optimised images, they are always the first place to look for performance improvements. In most websites the Largest Contentful Paint element is an image, and generally a huge amount of the total āweightā of pages are images.</p>
<p>There are a few different things you can and should do when optimising images:</p>
<ul>
<li>Compress images to reduce file size at the cost of quality. If a 85% quality image at 100kB looks identical to a 100% one at 2MB itās an easy choice</li>
<li>Convert to more recent image formats like WebP, AVIF or JpegXL. These formats may make certain images a lot smaller than their JPEG and PNG counterparts</li>
<li>Resize images to the exact size you need them, and use <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">responsive images</a> with <code>srcset</code> to offer smaller sizes. This way a browser doesnāt download a huge image it doesnāt need</li>
</ul>
<p>Manual processing tools like <a href="https://squoosh.app/">squoosh.app</a> make it easy to convert, resize, and optimise images by hand. A CMS may also have partial or full automatic optimisation, or you could consider an image CDN like <a href="https://cloudinary.com/">Cloudinary</a>, <a href="https://imgix.com/">Imgix</a> or <a href="https://www.cloudimage.io/">CloudImage</a> to help optimise and resize images on request.</p>
<p>You can also use <code>loading="lazy"</code> on your image elements to wait until theyāre in view and needed before downloading them, and <a href="https://web.dev/articles/fetch-priority">Priority Hints</a> with <code>fetchpriority="high"</code> to ask the browser to prioritise downloading your LCP image sooner.</p>
<h3 id="3-as-little-javascript-as-possible">3. As little JavaScript as possible</h3>
<p>JavaScript is the most expensive resource in terms of performance on the web, and is a really common bottleneck when used too much.</p>
<p>Where you can implement something in HTML or CSS then do, and make sure your pages can start rendering without JavaScript.</p>
<p>Avoid large JS libraries where you can and instead use built-in JavaScript APIs, progressive enhancement, or smaller and more performant libraries.</p>
<h3 id="4-watch-out-for-third-parties">4. Watch out for third-parties</h3>
<p>Third-party code, tag managers, analytics tools, embeds, and more can have a huge impact on performance. Where itās possible, consider removing these, or at least take measurements to make sure everyone is aware of their impact!</p>
<p>Youāll be amazed at the number of marketing departments that ask you to āfix their site speedā despite having seven tag managers enabled.</p>
<h3 id="5-cache-me-if-you-can">5. Cache me if you can</h3>
<p>If someone has visited your site and their browser has downloaded all the resources once before, should they really need to do so again?</p>
<p>You can <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching">use caching headers</a> to make repeat and subsequent visits a huge amount faster by reusing static assets like CSS and JavaScript instead of having to re-download them frequently.</p>
<h3 id="6-be-bold-optimise-your-fonts">6. Be bold, optimise your fonts</h3>
<p>One of the biggest culprits for Cumulative Layout Shift issues is due to web fonts. With a poorly optimised web font you may find your website loading almost entirely with a fallback font or invisible text, and then the entire page shifting as the font kicks in at a different size.</p>
<p>In an ideal performance world you could consider using a system font to avoid loading web fonts at all. In most cases though we can keep the web font but optimise it to make it load faster and more smoothly!</p>
<ul>
<li>Serve the font from your origin/domain instead of a third-party</li>
<li>Limit the number of font files you need to load by reducing the number of weights you use. Do you really need a regular, medium and a semibold?</li>
<li>Use <code>font-display:swap;</code> within <code>@font-face</code> to smooth the switch from fallback to custom fonts</li>
<li>Subset fonts to just the characters you need with a tool like <a href="https://github.com/zachleat/glyphhanger">glyphhanger</a>. If you have a website in only ASCII characters you can reduce your font sizes by up to 50%!</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide">Variable Fonts</a> combine multiple font weights and styles into a single font file. Whilst it may be larger than a single non-variable font, itāll generally be smaller than 3 traditional fonts. Go variable it if you can!</li>
<li>Preload the most important font or two using <code><link rel="preload" as="font"></code></li>
</ul>
<p>Depending on the licence of your font you may not be able to do all of the above ā particularly subsetting or loading from your server ā so make sure you double-check the font licence first. As Google Fonts are open-source you can do all of the above!</p>
<p><a href="https://www.zachleat.com/web/comprehensive-webfonts/">Zach Leatherman</a> and <a href="https://csswizardry.com/2020/05/the-fastest-google-fonts/">Harry Roberts</a> have some fantastic further reading for font optimisation.</p>
<h3 id="7-server-side-speed-shenanigans">7. Server side speed shenanigans</h3>
<p>This is one for the full-stackers, back-enders, all-rounders, and devops!</p>
<p>If your website relies on a single, slow server then you may have bottlenecked your site performance on your server speed. If it takes 2 seconds before the browser receives the HTML to render then no matter how much you do on the front-end thatās going to be your limit. You can get an idea of this by looking at the Time to First Byte on a fast connection close to the server. It depends on the systems and work being done but you want to be as close to 0ms as possible, definitely under 500ms.</p>
<p>You can improve this by optimising your servers and code, using a CDN like <a href="https://bunny.net/pricing/">Bunny CDN</a> or <a href="https://www.fastly.com/">Fastly</a> to cache HTML thatās the same for every user, or by speaking to your web host.</p>
<h3 id="8-stop-sharding-or-too-many-origins">8. Stop sharding, or too many origins</h3>
<p>Back in the day it was good advice to split up your assets across multiple origins/domains to deal with connection limits in HTTP 1, and to use public CDNs for shared caching. That hasnāt been good advice for a long time now since HTTP/2. Itās time to get everything on one origin!</p>
<p>Put as many assets on your main domain as possible. HTTP/2 or 3 can easily handle those resources and it avoids extra connections to new domains which takes valuable time.</p>
<p>Browsers used to allow websites to re-use a cached resource with the same URL across different domains. This provided a way to track users however so <a href="https://www.stefanjudis.com/notes/say-goodbye-to-resource-caching-across-sites-and-domains/">all browsers now separate the cache of each origin</a>. Using a public CDN no longer gives any benefit to resource caching.</p>
<h3 id="9-split-that-code">9. Split that code</h3>
<p>If you have some CSS or JavaScript that runs on only a single page, consider loading that separately only when needed, rather than across the entire website. It will improve the speed of all other pages, and avoid the extra cost of downloading something that a user might never use!</p>
<h3 id="10-consider-preloads-but-here-be-dragons">10. Consider preloads, but here be dragons</h3>
<p>Preloads are a way of having more control over how the browser loads resources. You can use them to ask for certain assets to be downloaded in advance of when theyāre needed, or to make the browser prioritise one asset over another.</p>
<p>Be careful with them however, preloading one asset will always delay the loading of something else so it can be very easy to accidentally make things worse!</p>
<p>Preloads are particularly handy for font files, where the browser wonāt start downloading them until the CSS has downloaded and rendering has begun. It can also be used for images or other assets that youāre finding are discovered too late.</p>
<p><a href="https://web.dev/articles/preload-critical-assets">Web.dev has an article about preloading critical assets</a>.</p>
<h2 id="what-next">What next?</h2>
<p>Now my friends, go forth and make fast websites! I hope that youāve gained more knowledge, confidence, understanding, and resources to test, diagnose and make websites perform better.</p>
<p>This is just the beginning, and there are loads of fantastic resources and people to follow to further your journey to Practised Performance Prophet!</p>
<h3 id="courses">Courses</h3>
<ul>
<li><a href="https://www.webpagetest.org/learn/lightning-fast-web-performance/">Lightning Fast Web Performance</a> by Scott Jehl</li>
<li><a href="https://csswizardry.gumroad.com/l/eihdtmcwf">Everything I Have Done to Make CSS Wizardry Fast</a> by Harry Roberts</li>
</ul>
<h3 id="books">Books</h3>
<ul>
<li><a href="https://abookapart.com/products/responsible-javascript">Responsible JavaScript</a> by Jeremy Wagner ā Book</li>
<li><a href="https://www.smashingmagazine.com/2021/04/image-optimization-pre-release/">Image Optimization</a> by Addy Osmani ā Book</li>
</ul>
<h3 id="blogs">Blogs</h3>
<ul>
<li><a href="https://web.dev/explore/metrics">Google's web.dev</a></li>
<li><a href="https://csswizardry.com/archive/">CSS Wizardry by Harry Roberts</a></li>
<li><a href="https://www.zachleat.com/">Zach Leatherman</a></li>
<li><a href="https://jakearchibald.com/">Jake Archibald</a></li>
<li><a href="https://sia.codes/">Sia Karamalegos</a></li>
<li><a href="https://calibreapp.com/blog">Calibre</a></li>
<li><a href="https://www.speedcurve.com/blog/">SpeedCurve</a></li>
<li><a href="https://cloudfour.com/">Cloudfour</a></li>
<li><a href="https://calendar.perfplanet.com/">Web Performance Calendar</a></li>
<li><a href="https://www.debugbear.com/blog">DebugBear</a></li>
</ul>
HTML: The Bad Parts
2023-12-13T00:00:00Z
https://htmhell.dev/adventcalendar/2023/13/
by Mayank<br><p>You've probably heard statements along the lines of "HTML is already accessible by default" or "You don't need to reinvent this perfectly fine HTML control". I consider these to be more of general claims rather than universal truths. It's extremely important for web developers to recognize gaps in the platform. To that end, I've decided to collect a few instances where HTML falls short, through accessibility issues or usability issues.</p>
<p>This is not a comprehensive list, and it does not include gaps in ARIA. I wanted to find a balance between widely known issues and more frequently encountered (but lesser known) ones, while making sure to include some things that we take for granted. In each section, I will include its severity, alternate suggestions, and links where you can find more detailed information.</p>
<h2 id="lessselect-multiplegreater"><code><select multiple></code></h2>
<p>Let's start with an easy one. The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#multiple"><code>multiple</code></a> attribute on <code><select></code> <a href="https://www.24a11y.com/2019/select-your-poison-part-2/#:~:text=around%20this%20behavior.-,Multi%2Dselect,-Multi%2Dselect%20Variant">should pretty much never be used</a>. It's like the polar opposite of single <code><select></code>, where instead of universal familiarity, it has universal unfamiliarity. Perhaps its only saving grace is that I have yet to encounter this attribute in any codebase.</p>
<p>To quote a screen-reader user (from Sarah Higley's excellent article linked below):</p>
<blockquote>This is a broken listbox. This is entirely broken, I donāt see any way of doing anything with this.</blockquote>
<p><strong>Impact</strong>: High. There is a good chance most of your users will not know how to navigate this monstrous control.</p>
<p><strong>What to use instead</strong>: A list of checkboxes is a totally fine alternative; or you could build a custom multi-selectable listbox.</p>
<p><strong>Further reading</strong>: <a href="https://www.24a11y.com/2019/select-your-poison-part-2"><code><select></code> your poison part 2: test all the things</a> (article by Sarah Higley).</p>
<h2 id="lessigreater-(for-icon)"><code><i></code> (for icon)</h2>
<p>Let's do another easy one to warm up. The <code><i></code> element is <a href="https://w3c.github.io/html-aam/#el-i">semantically insignificant</a>, so it's not necessarily ābadā if used for regular text content. More frequently though, it is used for icon fonts.</p>
<p>I wouldn't even normally include this in the list, since this is more of a developer error than a platform issue. However, it is 2023 and I still regularly encounter the <code><i></code> element being used with something like FontAwesome. This is problematic mainly because it involves using a web font to visually substitute text characters (which may have one meaning) with icons (which are completely unrelated to the text characters). As a result, the text characters, even though they are generated in CSS, are part of the page's content and will be included in the accessibility tree by default. In addition to that, it looks bad, breaks often (and in <a href="https://cloudfour.com/thinks/seriously-dont-use-icon-fonts/#they-encroach-on-emoji-turf">hilarious ways</a> š“), and leads to other accessibility issues. You can learn more about all of these issues in Tyler Sticka's post linked below.</p>
<p><strong>Impact</strong>: Low to medium. You could make it less inaccessible by excluding it from the accessibility tree (using <code>aria-hidden</code>) and providing a text alternative (using <a href="https://www.tpgi.com/the-anatomy-of-visually-hidden/">visually hidden</a> text), but it will still remain problematic.</p>
<p><strong>What to use instead</strong>: SVGs! They are well supported, and better in many important ways. Unlike icon fonts, SVGs are properly designed to display graphics, so they will not result in unexpected text being added to the accessibility tree. There are tons of different ways to use them too, so you can have your pick, whether you like inlining, using sprites, or keeping it within CSS (using <a href="https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images"><code>mask</code></a>).</p>
<p>When using <code><svg></code> elements, a reliable pattern is to use <code>aria-hidden</code> alongside an additional text alternative (which may be visually hidden if needed). For example, the markup for a button might look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>use</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>#heart<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Favorite<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p><strong>Further reading</strong>: <a href="https://cloudfour.com/thinks/seriously-dont-use-icon-fonts/">Seriously, Don't Use Icon Fonts</a> (article by Tyler Sticka)</p>
<h2 id="lessulgreater-and-lessolgreater"><code><ul></code> and <code><ol></code></h2>
<p>I can't make a list (heh!) of problematic HTML and not include lists in there.</p>
<p>I'm not going to say ādon't use listsā; lists are very useful in many scenarios. But developers have a tendency to <a href="https://almanac.httparchive.org/en/2022/markup#top-elements">overuse lists</a>, often just to remove list styles anyway. Sometimes we may even slap an ARIA role on it (thus overriding the list semantics), or worse, add non-<code><li></code> children for styling purposes or otherwise (thus producing invalid markup). In all of these scenarios, we are losing any benefit provided by list elements, and potentially introducing new issues.</p>
<p>Safari has noticed this overuse of lists, dubbing it "list-itis", and has started removing list semantics in certain cases; lists that are outside a <code>nav</code> and are styled with <code>list-style: none</code> are <em>not</em> considered to be lists by Safari. While this change was surely done with good intentions, it has the unfortunate effect of making even legitimately styled lists purely presentational.</p>
<p>In addition to all of this, ordered lists have their own oddities. Browsers consider the item numbers to be "presentational", so when selecting and copying a list, the numbers are not included in the plain-text clipboard entry. This breaks user expectations, especially when the numbers are important, as in the case of legal documents.</p>
<p><strong>Impact</strong>: Low to medium. I think it's totally fine to let Safari/VoiceOver do its thing; in fact, their users are likely used to it and may even expect an almost list-free web. The clipboard issue with ordered lists can be quite serious, but there are workarounds.</p>
<p><strong>What to use instead</strong>: If you really need to preserve list semantics, <code>role="list"</code> can be added to <code><ul></code>. If you're already using a different role, then plain <code><div></code>s should work fine. <a href="https://benmyers.dev/blog/on-the-dl/"><code><dl></code></a> is another potential alternative for key-value pairs.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>If the list item numbers are important, consider using unordered lists where the numbers are part of the text content.</p>
<p><strong>Further reading</strong>: <a href="https://www.scottohara.me/blog/2019/01/12/lists-and-safari.html">"Fixing" Lists</a> (article by Scott O'Hara), <a href="https://threadreaderapp.com/thread/1337226933822603270.html">Twitter thread</a> by Apple's James Craig, and <a href="https://siderea.dreamwidth.org/1819759.html">"I Blame the W3C's HTML Standard for Ordered Lists"</a> (article by Sibylla Bostoniensis).</p>
<h2 id="title-attribute"><code>title</code> attribute</h2>
<p>The <code>title</code> attribute is the one that frustrates me the most, particularly because of its long history. It is so bad that the <a href="https://www.w3.org/TR/html/dom.html#the-title-attribute">HTML spec</a> explicitly warns against using it. And yet I've seen this attribute used in almost every codebase I've worked with.</p>
<p>Some of the many issues with <code>title</code> include:</p>
<ul>
<li>Sighted keyboard users have absolutely no way to access the <code>title</code> content.</li>
<li>Touch-screen users also cannot access the <code>title</code> content.</li>
<li>The default tooltip styling cannot be changed in any way.</li>
<li>The trigger needs to be hovered for an arbitrarily long delay before it appears.</li>
<li>The tooltip cannot be dismissed without moving the pointer, and therefore can obscure other parts of the page.</li>
<li>The tooltip cannot be hovered over, meaning the mouse needs to keep hovering the trigger element. As a result, the tooltip could become obscured by the mouse pointer, its text will not be selectable, and screen-magnifier users may find it impossible to view it.</li>
<li>The text inside the tooltip does not scale with the user's zoom level.</li>
<li>Long text inside the tooltip cannot be made to wrap; in some browsers, it will become as wide as the screen itself (even escaping the browser window bounds) before it starts wrapping.</li>
<li>There is no way to indicate if the <code>title</code> content should be part of the element's accessible name or description (or neither).</li>
<li>Screen-readers do not consistently announce the <code>title</code> content on all elements.</li>
</ul>
<p><strong>Impact</strong>: High. By relying on this attribute, you are making the intentional choice of leaving out a large percentage of your users.</p>
<p><strong>What to use instead</strong>: In many cases, including the text as part of the element's content can suffice; this text could be visually-hidden if necessary. In other cases, <code>aria-labelledby</code> or <code>aria-describedby</code> will work better than <code>title</code>. One exception is iframes, which need to be labeled using <code>title</code>.</p>
<p>In almost all cases, a <a href="https://www.mayank.co/blog/tooltip-using-webcomponents">custom tooltip</a>, in addition to the techniques described above, would also work better than <code>title</code>. However, tooltips come with their <a href="https://adamsilver.io/blog/the-problem-with-tooltips-and-what-to-do-instead/">own set of problems</a>, so an even better solution would be to redesign the interface to avoid needing tooltips altogether. For example, you could <em>always</em> show the text or use a <a href="https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/">disclosure pattern</a>.</p>
<p><strong>Further reading</strong>: <a href="https://www.24a11y.com/2017/the-trials-and-tribulations-of-the-title-attribute/">The Trials and Tribulations of the Title Attribute</a> (article by Scott O'Hara), and <a href="https://www.tpgi.com/using-the-html-title-attribute/">Using the HTML title attribute</a> (article by Steve Faulkner).</p>
<h2 id="lessdatalistgreater"><code><datalist></code></h2>
<p>At first, the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist"><code>datalist</code> element</a> sounds like a promising alternative to custom comboboxes. The more you use it though, the more disappointed you'll find yourself.</p>
<ul>
<li>This element has very questionable default styling.
<ul>
<li>On Chrome desktop, it appears as a weirdly offset popover that can drastically shift its position while the user is typing; at the same time, it becomes detached from the input when the user scrolls away.</li>
<li>On Chrome Android, it appears above the virtual keyboard (rather than on the page) and only in portrait mode.</li>
</ul>
</li>
<li>Its appearance cannot be customized at all. It does not even respect the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme"><code>color-scheme</code></a>.</li>
<li>It does not respect the user's font size or zoom level.</li>
<li>It generally has good support on desktop screen-readers, but on mobile it requires navigating to the very end of the page to interact with any options.</li>
<li>It does not work on Firefox Android at all!</li>
</ul>
<p><strong>Impact</strong>: Medium to high. At best, this element can be useful as an optional hint for input fields that would still be perfectly usable without the added help from autocompleting options.</p>
<p><strong>What to use instead</strong>: <code><select></code> or a custom combobox. š¤·</p>
<p><strong>Further reading</strong>: <a href="https://adrianroselli.com/2023/06/under-engineered-comboboxen.html">Under-Engineered Comboboxen?</a> (article by Adrian Roselli)</p>
<h2 id="lessinput-placeholdergreater"><code><input placeholder></code></h2>
<p>Placeholder text is intended to <em>help</em> the user by providing a hint when an input is empty. In practice, it often ends up doing the opposite.</p>
<p>Placeholder text is usually prone to color contrast issues. If you try fixing color contrast, it may end up looking indistinguishable from real input values. If you put important information in it (like password requirements, or god forbid, the label itself), the input value will hide this important information as soon as the user starts typing.</p>
<p><strong>Impact</strong>: Medium to high. At best, it may be ignored. At worst, it may frustrate your users and impact your revenue.</p>
<p><strong>What to use instead</strong>: Nothing? A prominently visible label may be sufficient. If you do need hint text, consider placing it outside the input, and associating it using <code>aria-describedby</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>phone<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Phone number<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>phone<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>phone-hint<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>phone-hint<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Example: 123-456-7890<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p><strong>Further reading</strong>: <a href="https://www.smashingmagazine.com/2018/06/placeholder-attribute/">Don't Use The Placeholder Attribute</a> (article by Eric Bailey)</p>
<h2 id="lessinput-type=numbergreater"><code><input type=number></code></h2>
<p>Let's cut to the chase: number inputs will increment/decrement when using scroll wheel or gesture or arrow keys, making it extremely prone to accidents. Yikes!</p>
<p>And that's in addition to its other accessibility, consistency, and styling issues.</p>
<ul>
<li>Some browsers will silently discard non-numeric user input without any feedback.</li>
<li>Other browsers will allow entering arbitrary numbers but fire events with empty values, thus losing user-entered data.</li>
<li>The "spin" buttons are extremely small and difficult to click.</li>
<li>The "spin" buttons cannot be styled (beyond simple color scheme).</li>
</ul>
<p><strong>Impact</strong>: High. The scrolling issue alone is enough to make this a dealbreaker, as users could <a href="https://bradfrost.com/blog/post/you-probably-dont-need-input-typenumber/">enter the wrong number without even realizing</a>.</p>
<p><strong>What to use instead</strong>: <code><input inputmode="numeric" pattern="[0-9]*"></code>. The <a href="https://better-mobile-inputs.netlify.app/"><code>inputmode</code></a> attribute helps show a nicer keyboard on touchscreen devices, and the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern"><code>pattern</code></a> attribute helps with validation.</p>
<p><strong>Further reading</strong>: <a href="https://technology.blog.gov.uk/2020/02/24/why-the-gov-uk-design-system-team-changed-the-input-type-for-numbers/">Why the GOV.UK Design System team changed the input type for numbers</a></p>
<h2 id="lessinput-type=dategreater"><code><input type=date></code></h2>
<p>Native date pickers have many browser inconsistencies, with some parts of them being inaccessible.</p>
<ul>
<li>On Chrome desktop, the calendar button cannot be tabbed to.</li>
<li>On Safari desktop, the date picker cannot be closed using keyboard.</li>
<li>On Safari desktop, the date picker is minuscule and cannot be scaled up.</li>
<li>On Chrome and Firefox Android, the date input cannot be typed into, forcing the use of the clunky calendar UI even if the date is familiar to the user.</li>
</ul>
<p>In all environments, the date format is shown as placeholder text which, as we've already established above, is problematic. The browser determines the date format, and it may choose something strange (like the US date format), leading to unnecessary confusion.</p>
<p><strong>Impact</strong>: High. These inconsistencies are not just minor differences, they introduce real barriers to access.</p>
<p><strong>What to use instead</strong>: Honestly, use simple text inputs, or even selects in some cases.</p>
<p>You can also use separate inputs for date/month/year and group them with a fieldset. This fieldset could even be styled to look like <a href="https://russmaxdesign.github.io/date-picker-input/">one contiguous form field</a>. If you choose to hide the input labels though, I would also suggest showing the date format outside the inputs (as part of the legend or below the inputs).</p>
<p>Custom date pickers are notoriously hard to get right, so if you choose to one, it should be optional and in addition to the text inputs.</p>
<p><strong>Further <s>reading</s> watching</strong>: <a href="https://www.youtube.com/watch?v=D2Gy2WN4Iys">What makes an accessible date picker? Is it even possible?</a> (talk by Russ Weakley)</p>
<h2 id="lessmenugreater"><code><menu></code></h2>
<p>Just don't. Whatever you expect this element does⦠it probably doesn't. The <code><menu></code> element was originally intended to be a "context menu" but it never got anywhere, and now it is replaced by <code><ul></code>.</p>
<p><strong>Impact</strong>: Low. <code><menu></code> is simply remapped to <code><ul></code>.</p>
<p><strong>What to use instead</strong>: Because the term āmenuā is so overloaded, it depends on what you're trying to accomplish. Some alternatives include:</p>
<ul>
<li><code><ul></code>(or <code>role="list"</code>) when you have a list.</li>
<li><code>role="menu"</code> when you have a desktop-like menu.</li>
<li><code>role="listbox"</code> when you have something resembling a custom <code><select></code>.</li>
<li><code><dialog></code> or <code>role="dialog"</code> when you have a generic popover (perhaps with a <code>role="list"</code> inside).</li>
</ul>
<p>Do note that some of these roles require custom JavaScript interactions to be wired up.</p>
<p><strong>Further reading</strong>: <a href="https://adrianroselli.com/2023/05/be-careful-using-menu.html">Be Careful Using 'Menu'</a> (article by Adrian Roselli)</p>
<h2 id="lessbutton-disabledgreater"><code><button disabled></code></h2>
<p>Disabled buttons are strangely overused, perhaps due to their familiarity. Two common scenarios for disabled buttons include:</p>
<ul>
<li>when an action is temporarily unavailable, perhaps due to requiring some prerequisite task (such as selecting an item or filling a form correctly).</li>
<li>when submitting a form, to prevent duplicate submissions.</li>
</ul>
<p>However, the <code>disabled</code> attribute does more than just prevent clicks. It also prevents hover and focus capabilities. This has many negative ramifications. Disabled buttons cannot show tooltips (which may be important for explaining why the button is disabled). Keyboard users cannot <kbd>Tab</kbd> to disabled buttons. If a button suddenly becomes disabled (e.g. when submitting a form), focus could get lost and create a confusing experience. Additionally, disabled buttons are exempt from <a href="https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html">color contrast requirements</a>, increasing the likelihood that it will have poor contrast.</p>
<p><strong>Impact</strong>: High. Disabled buttons selectively serve a small percentage of your user-base (and not very well either).</p>
<p><strong>What to use instead</strong>: Use <code>aria-disabled="true"</code> and manually disable clicks. If possible though, consider reworking the design to avoid needing disabled buttons altogether.</p>
<p><strong>Further reading</strong>: <a href="https://css-tricks.com/making-disabled-buttons-more-inclusive/">Making Disabled Buttons More Inclusive</a> (article by Sandrina Pereira)</p>
<h2 id="lessvideogreater"><code><video></code></h2>
<p>The native video player still has many accessibility and usability issues. For keyboard users, focus is a recurring issue across browsers, because not all controls can be reached, and the controls disappear once the video starts playing. For screen reader users, the experience is lacking in some areas, especially for more "advanced" features like picture-in-picture mode.</p>
<p>I highly recommend checking out Adrian's article below for a deep-dive on support across various browsers and screen-reader pairings.</p>
<p><strong>Impact</strong>: Medium to high.</p>
<p><strong>What to use instead</strong>: Unsure. Maybe a custom video player, if it's built with accessibility in mind. Regardless of what you use, please include captions and a transcript, and provide a way to download the video for offline viewing in the user's player of choice. Oh and please for the love of your users, <a href="https://www.boia.org/blog/why-autoplay-is-an-accessibility-no-no">do not use <code>autoplay</code></a>.</p>
<p><strong>Further reading</strong>: <a href="https://adrianroselli.com/2023/09/browser-video-players-review.html">Browser Video Players Review</a> (article by Adrian Roselli)</p>
<h2 id="link-protocols-(tel-mailto)">Link protocols (<code>tel:</code>, <code>mailto:</code>)</h2>
<p>Links on the web can have a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler/Web-based_protocol_handlers">non-http protocol</a> like <code>mailto:</code> or <code>tel:</code>. These protocols are commonly used as a supposed way to help the users (or spammers) send an email or dial a number. However, this makes a very bold assumption is that the user is visiting the webpage on the same device that will be used to send the email or dial a number and wants to use the default application to do so. This can lead to frustration when the user is just looking for a way to copy the number or dial it from a different device. To make it worse, the actual address/number often gets hidden underneath the link, instead displaying something useless like "Email me".</p>
<p><strong>Impact</strong>: Low to medium. It's confusing and annoying, but not the end of world I suppose. Users will (reluctantly) find workarounds.</p>
<p><strong>What to use instead</strong>: Just show the actual email address or phone number. It is trivial to copy or manually transfer it over to the desired device. Additionally, many devices will automatically display a prompt when selecting text that can be recognized as an email/number.</p>
<p><strong>Further reading</strong>: <a href="https://bkardell.com/blog/tel-me.html">tel:me.about.it.</a> (article by Brian Kardell)</p>
<hr />
<h2 id="wrapping-up">Wrapping up</h2>
<p>In closing, I just want to reiterate that accessibility is not as simple as "just use HTML". Even with best intentions and pristine HTML source, we may end up with inaccessible UIs if we don't do our due diligence.</p>
<p>I'm sure this list will look different (hopefully shorter) 10 years from now. As more people start to take interest in these things, it will create more awareness. It will push standards bodies and browser vendors to improve the platform and get us closer to a more "accessible by default" web. You can personally help move the needle by opening new bugs or by commenting on existing ones:</p>
<ul>
<li>Chrome/Blink: <a href="https://bugs.chromium.org/p/chromium/">https://bugs.chromium.org/p/chromium/</a></li>
<li>Firefox/Gecko: <a href="https://bugzilla.mozilla.org/">https://bugzilla.mozilla.org/</a></li>
<li>Safari/WebKit: <a href="https://bugs.webkit.org/">https://bugs.webkit.org/</a></li>
</ul>
Test-driven HTML and accessibility
2023-12-12T00:00:00Z
https://htmhell.dev/adventcalendar/2023/12/
by David Luhr<br><p>When I started writing unit tests and following a <a href="https://en.wikipedia.org/wiki/Test-driven_development">test-driven development (TDD)</a> workflow, I was stoked with the immediate feedback and confidence I gained in every line of JavaScript I wrote.</p>
<p>TDD improved my software design with simpler, more predictable code. It saved me countless hours of manual debugging by reporting errors directly in failing tests. And with solid test coverage, it allowed me to add new logic and functionality while keeping everything in an always-working state.</p>
<p>I followed the process of <a href="https://www.jamesshore.com/v2/blog/2005/red-green-refactor">Red, Green, Refactor</a>, meaning I wrote a failing test, wrote just enough code to make the test pass, and then freely cleaned up my code, knowing everything was working. My JavaScript code had never been better.</p>
<p>Then one day, I wrote a UI-oriented test that used <code>document.querySelector()</code>. Before the test even finished running, it errored and revealed that JavaScript tests run outside of the browser in Node, where no document object model (DOM) exists. Since the focus of my career is evaluating, designing, and developing accessible user interfaces, primarily with HTML and CSS, I wanted to apply TDD techniques to HTML and accessibility. But it seemed like my dream testing setup wasn't possible.</p>
<p>Months passed of continuing to enjoy TDD with JavaScript, while wishing I could do more to test my accessible markup. Most of my research pointed to slow, flaky approaches like scripting an entire <a href="https://en.wikipedia.org/wiki/Headless_browser">headless browser</a> just to check if an HTML element rendered or if a menu opened on click.</p>
<p>Other testing libraries use synthetic (faked) interactions by dispatching DOM events that might lead to false positives in tests if they don't accurately replicate real user interaction. Plus, these libraries don't have the necessary DOM events for all interactions. I wanted to write small, focused, fast unit tests that provided trustworthy, immediate feedback with every change I made.</p>
<p>Then I came across a tool called <a href="https://modern-web.dev/docs/test-runner/overview/">Web test runner</a> from the team behind <a href="https://open-wc.org/">Open Web Components</a>. Instead of running JavaScript tests in Node, Web Test Runner executes tests directly in the browser.</p>
<p>This is a big deal for multiple reasons:</p>
<ol>
<li>Tests run your production JavaScript code in the production environmentāthe browser</li>
<li>Tests have access to the real DOM, unlocking the ability to write tests for HTML, accessibility, and even CSS</li>
<li>Tests can send native interactions, such as actual mouse clicks and keyboard input, instead of faking them with DOM events</li>
<li>And it's even possible do things like set the viewport dimensions to test responsive design</li>
</ol>
<h2 id="shift-accessibility-feedback-earlier">Shift accessibility feedback earlier</h2>
<p>With this approach, I write tests to capture my expectations around semantic markup and accessibility upfront, then get instant feedback on whether my code meets those expectations.</p>
<p>Let's say I expect paragraph (<code><p></code>) elements shouldn't be empty. I can write the following test, which gets all <code><p></code> elements, loops over them, and expects each one doesn't have empty text content:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"paragraphs"</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> docAllParagraphs <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"p"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"have text content"</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></span><br /><span class="highlight-line"> docAllParagraphs<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">paragraph</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>paragraph<span class="token punctuation">.</span>textContent<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>not<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>With access to the real DOM in tests, I can continuously check if I'm using heading levels, ARIA, and landmark regions correctly. I can build custom UI patterns and ensure they respond to keyboard input and manage focus as expected. And as I add new features or refactor my code, my tests re-run with every change to make sure I don't harm accessibility as the project evolves.</p>
<p>Accessibility-focused linters give feedback as you code, but have limited awareness outside of very specific violations. Automated accessibility audits and most manual review happens after you write the code. Test-driven accessibility is about providing continuous feedback before, during, and after you write the code.</p>
<p>Using all these forms of accessibility testing together provides the most value. Unit testing HTML allows me find, fix, and prevent accessibility issues as I'm working. It also allows me to focus my manual accessibility reviews on more holistic evaluations, such as navigating through a page with a screen reader to asses the user experience.</p>
<p>This test-driven approach makes something possible that I've been striving for in my work: an accessibility-first workflow. With an accessibility-first mindset, we plan for and preserve accessibility from the beginning of a project instead of leaving it as an afterthought. If I could achieve one thing in my career, it would be to make accessibility-first a foundational practice alongside usability, responsive design, performance, and security.</p>
<h2 id="evolve-code-with-confidence">Evolve code with confidence</h2>
<p>In addition to getting feedback upfront and continuously thereafter, having tests allows us to change our code freely as features and user needs evolve.</p>
<p>The downside of automated accessibility testing tools and manual review is they can't be performed continuously, and often focus on the current page in the browser. We either have to manually re-test with each change we make exhaustively across all pages, or we depend on getting feedback after batches of changes.</p>
<p>With tests in place, we can keep our code (and accessibility) in an always-working state. Every time we make a change, our tests run across the entire codebase. If our tests pass, we can more confidently and frequently deploy our changes to users.</p>
<h2 id="the-setup">The setup</h2>
<p>To begin testing our HTML in the browser, we first install our dependencies:</p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line"><span class="token function">npm</span> i --save-dev @web/test-runner @web/test-runner-mocha @esm-bundle/chai @web/test-runner-commands @web/test-runner-playwright</span></code></pre>
<p>We need <a href="https://modern-web.dev/docs/test-runner/overview/">Web test runner</a> to run tests in the browser, Web Test Runner's implementation of Mocha for directly testing HTML files, Chai for test assertions, Web Test Runner commands for sending native interactions (click, keyboard, etc.) to our UI elements, and Web Test Runner's implementation of Playwright for headless browsers to run the tests in.</p>
<p>With this setup, we run our tests in Chromium, Firefox, and WebKit all at once to make sure our code works across the major browsers. This happens super quickly in the background and works in different operating systems.</p>
<p>Even through browser cross-compatibility continues to improve, this testing setup reveals discrepancies that would be incredibly difficult to otherwise detect. For example, I recently found that getting the value of an element's <code>role</code> attribute with <code>Element.role</code> was failing only in Firefox, and using <code>Element.getAttribute("role")</code> was the fix. I'm not sure if or how I would have found that issue without tests that run directly in each of the browsers.</p>
<p>Next, we add a couple scripts to our <code>package.json</code> to run tests either in a single pass or in watch mode:</p>
<pre class="language-json"><code class="language-json"><span class="highlight-line"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">"test"</span><span class="token operator">:</span> <span class="token string">"web-test-runner \"/**/*.test.js\" --node-resolve --playwright --browsers chromium firefox webkit"</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token property">"test:watch"</span><span class="token operator">:</span> <span class="token string">"web-test-runner \"/**/*.test.js\" --node-resolve --playwright --browsers chromium firefox webkit --watch"</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">,</span></span></code></pre>
<p>Last, we can run our watch mode script as we work. This will run all tests, then detect any file changes and re-run only the impacted tests:</p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line"><span class="token function">npm</span> run test:watch</span></code></pre>
<p>If our website is made with simple HTML pages, we can run the tests directly in the HTML files (with a <code><script></code> tag) to test specific markup. If our HTML is rendered with JavaScript, as is the case with web components and many other approaches, then we can test the rendering in JS files alongside any logic or functionality that we'd usually unit test. We'll compare the tradeoffs of these two options at the end of this article.</p>
<h2 id="writing-tests">Writing tests</h2>
<p>To demonstrate the value of this setup, let's write tests to prevent some of the most common (non-visual) WCAG 2 failures found in the annual <a href="https://webaim.org/projects/million/#wcag">WebAIM Million study</a>:</p>
<ul>
<li>Low contrast text</li>
<li>Missing alt text</li>
<li>Empty links</li>
<li>Missing form input labels</li>
<li>Empty buttons</li>
<li>Missing document language</li>
</ul>
<p>Writing tests for low contrast text is possible but too involved for this post. So let's focus on tests for the remaining most common failures.</p>
<p>First, we'll create a test file called <code>example.test.js</code> with the required imports:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">import</span> <span class="token punctuation">{</span> runTests <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@web/test-runner-mocha"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">import</span> <span class="token punctuation">{</span> expect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@esm-bundle/chai"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">import</span> <span class="token punctuation">{</span> sendKeys <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@web/test-runner-commands"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token function">runTests</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></span><br /><span class="highlight-line"> <span class="token comment">// TODO: Write tests here</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p><code>runTests</code> allows us to directly test HTML files, which makes this setup quick and easy for static HTML. <code>@web/test-runner-commands</code> provides native interaction utilities, such as <code>sendKeys</code>, that we'll use in a later example.</p>
<p>Then we'll create an example HTML file called <code>example.html</code> with the usual boilerplate and link to the test file:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Our test file<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>module<span class="token punctuation">"</span></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>example.test.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- TODO: Write markup here --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="missing-alt-text">Missing alt text</h3>
<p>In <code>example.test.js</code>, let's write a test to check for missing image alt text:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token comment">// ...</span></span><br /><span class="highlight-line"><span class="token function">runTests</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></span><br /><span class="highlight-line"> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"images"</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> docAllImages <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"img"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"have an alt attribute"</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></span><br /><span class="highlight-line"> docAllImages<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">image</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>image<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"alt"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> image<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>This test gets all images in the DOM, loops over them, and checks to make sure each one has an <code>alt</code> attribute.</p>
<p>Error messages in JavaScript include the line and character numbers to help quickly locate and fix errors, but we don't have access to this with HTML elements. By passing <code>image.outerHTML</code> as the second argument of our <code>expect()</code> function, we can include the element in the error message to make finding and correcting the offending element easier. We'll use this for custom error messages any time we're checking all instances of an element.</p>
<p>If this is too verbose or noisy for more complex elements, we could instead only include the element's opening tag, or assign all elements unique <code>data-test-id</code> values to identify them directly in test error messages.</p>
<p>In our <code>example.html</code> file, let's add an image without an <code>alt</code> attribute to get a failing test:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/example.jpg<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>Web Test Runner reports the following in the command line:</p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line">ā images <span class="token operator">></span> have an alt attribute</span><br /><span class="highlight-line"> AssertionError: <span class="token operator"><</span>img <span class="token assign-left variable">src</span><span class="token operator">=</span><span class="token string">"/example.jpg"</span><span class="token operator">></span>: expected null to exist</span><br /><span class="highlight-line"> at example.test.js:9:41</span><br /><span class="highlight-line"></span><br /><span class="highlight-line">Chromium: <span class="token number">1</span> failed</span><br /><span class="highlight-line">Firefox: <span class="token number">1</span> failed</span><br /><span class="highlight-line">Webkit: <span class="token number">1</span> failed</span></code></pre>
<p>Our test fails as expected. To get the test to pass, we simply need to add an <code>alt</code> attribute, either as an empty string <code>""</code> for decorative content or with a useful description for meaningful content.</p>
<p>The neat thing about this test is it passes if no <code><img></code> elements exist, but fails if they exist and don't have an <code>alt</code> attribute. So, this test can be used from the beginning of any project without creating irrelevant noise.</p>
<p>Let's write tests for other common WCAG 2 failures.</p>
<h3 id="empty-links">Empty links</h3>
<p>We'll add another test in <code>example.test.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">runTests</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></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"links"</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> docAllLinks <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"a"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"have a non-empty href attribute"</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></span><br /><span class="highlight-line"> docAllLinks<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">link</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> hrefValue <span class="token operator">=</span> link<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"href"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>hrefValue<span class="token punctuation">,</span> link<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>hrefValue<span class="token punctuation">,</span> link<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>not<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"are not empty"</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></span><br /><span class="highlight-line"> docAllLinks<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">link</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>link<span class="token punctuation">.</span>textContent<span class="token punctuation">,</span> link<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>not<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>Similar to the previous test, we get all links from the DOM, loop over them, and check that their text content is not empty. We're also checking that links have a non-empty <code>href</code> attribute, just as an example of other HTML validation we'd like to do.</p>
<h3 id="missing-form-input-labels">Missing form input labels</h3>
<p>Time for another test suite:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">runTests</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></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"form inputs"</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> docAllFormInputs <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"input, textarea, select"</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"have a dedicated label"</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></span><br /><span class="highlight-line"> docAllFormInputs<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">formInput</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> inputId <span class="token operator">=</span> formInput<span class="token punctuation">.</span>id<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>inputId<span class="token punctuation">,</span> formInput<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token keyword">const</span> inputLabel <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">label[for="</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>inputId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"]</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>inputLabel<span class="token punctuation">,</span> formInput<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>inputLabel<span class="token punctuation">.</span>textContent<span class="token punctuation">,</span> formInput<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>not<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>In this test, we get all form input elements, loop over them, and assert multiple things:</p>
<ol>
<li>Each form input element needs an <code>id</code> attribute</li>
<li>We expect to find a <code><label></code> element with a <code>for</code> attribute that has the corresponding input's <code>id</code> as the value</li>
<li>We expect the <code><label></code> element to not be empty</li>
</ol>
<p>It's possible to provide an accessible name for form inputs with other techniques, such as <code>aria-label</code> or <code>aria-labelledby</code>, but I'm being opinionated here because I prefer actual <code><label></code> elements and text content. Testing for an accessible name through other means is just as easy.</p>
<h3 id="empty-buttons">Empty buttons</h3>
<p>This is nearly identical to our empty links test:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">runTests</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></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"buttons"</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> docAllButtons <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"button"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"are not empty"</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></span><br /><span class="highlight-line"> docAllButtons<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">button</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>button<span class="token punctuation">.</span>textContent<span class="token punctuation">,</span> button<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>not<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<h3 id="missing-document-language">Missing document language</h3>
<p>Let's write a test for the final common WCAG 2 failure:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">runTests</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></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"document"</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></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"has a set language"</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> languageValue <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"html"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"lang"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>languageValue<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token string">"en"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>This one is the most straightforward, but valuable nonetheless. If your site has internationalization, you'd want to check the <code>lang</code> value matches the current locale and updates based on user preference.</p>
<h2 id="testing-heading-levels">Testing heading levels</h2>
<p>We're starting to build a universal test suite that will preserve accessibility across our projects.</p>
<p>Let's add some further assertions around heading levels, which are a common source of errors in accessibility audits:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">runTests</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></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"headings"</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> docAllHeadings <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"h1, h2, h3, h4, h5, h6"</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"have <h1> element as the first heading"</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></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token function">getHeadingLevel</span><span class="token punctuation">(</span>docAllHeadings<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> docAllHeadings<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>outerHTML</span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"have a single <h1>"</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></span><br /><span class="highlight-line"> docAllHeadings<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">heading<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// Don't fail the test if the first heading on the page is `<h1>`</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>index <span class="token operator">===</span> <span class="token number">0</span> <span class="token operator">&&</span> <span class="token function">getHeadingLevel</span><span class="token punctuation">(</span>heading<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">return</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span><span class="token function">getHeadingLevel</span><span class="token punctuation">(</span>heading<span class="token punctuation">)</span><span class="token punctuation">,</span> heading<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>not<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"don't skip heading levels"</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></span><br /><span class="highlight-line"> docAllHeadings<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">heading<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">let</span> previousHeadingLevel <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> currentHeadingLevel <span class="token operator">=</span> <span class="token function">getHeadingLevel</span><span class="token punctuation">(</span>heading<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>index <span class="token operator">!==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> previousHeadingLevel <span class="token operator">=</span> <span class="token function">getHeadingLevel</span><span class="token punctuation">(</span>docAllHeadings<span class="token punctuation">[</span>index <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>currentHeadingLevel<span class="token punctuation">,</span> heading<span class="token punctuation">.</span>outerHTML<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>be<span class="token punctuation">.</span><span class="token function">lessThanOrEqual</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> previousHeadingLevel <span class="token operator">+</span> <span class="token number">1</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">getHeadingLevel</span><span class="token punctuation">(</span><span class="token parameter">heading</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">return</span> <span class="token operator">+</span>heading<span class="token punctuation">.</span>tagName<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">"h"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>With these tests, we can ensure heading levels are used properly in our page. We should only have a single <code><h1></code> heading. This heading should also be the first heading on the page. Lastly, headings should never skip levels, such as <code><h2></code> followed by <code><h4></code>. We make use of small utility function, <code>getHeadingLevel()</code>, to keep our code more concise and mistake-proof.</p>
<p>Although these tests enforce correct heading logic, they can't evaluate if heading levels are appropriate for the content they describe. That always requires thoughtful consideration. Tests free the developer to spend more time on wider UX considerations like this.</p>
<h2 id="testing-user-interaction">Testing user interaction</h2>
<p>So far, we've made simple assertions about our static HTML to make sure it's valid and not causing common accessibility failures. But as we create interactive patterns in our UI, the accessibility considerations become much more complex. We need to use HTML and ARIA to create custom semantics. We need to handle click, tap, and keyboard events. And we need to display and hide content, all while updating multiple attributes in concert.</p>
<p>Let's follow the Red, Green, Refactor workflow as we build a custom disclosure (show/hide) pattern.</p>
<p>We'll start with our tests in <code>example.test.js</code> to make sure our initial HTML and ARIA are correct:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">runTests</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></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"disclosure"</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> docDisclosureToggle <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[data-component="disclosureToggle"]</span><span class="token template-punctuation string">`</span></span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> docDisclosureContent <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[data-component="disclosureContent"]</span><span class="token template-punctuation string">`</span></span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"has a toggle button"</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></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureToggle<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureToggle<span class="token punctuation">.</span>tagName<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span><span class="token string">"BUTTON"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"has a toggle button with the expected ARIA attributes"</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></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureToggle<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureToggle<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token string">"false"</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureToggle<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-controls"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureToggle<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>not<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token string">""</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"has a content panel with the corresponding controls ID"</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></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureContent<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureContent<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> docDisclosureToggle<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-controls"</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"has a hidden content panel by default"</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></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureContent<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"hidden"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>These tests will fail, meaning we're in the "Red" stage. Now, we can author our initial HTML in <code>example.html</code> to get our tests to pass:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><br /><span class="highlight-line"> <span class="token attr-name">type</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><br /><span class="highlight-line"> <span class="token attr-name">aria-expanded</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><br /><span class="highlight-line"> <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>disclosureContent<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">data-component</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>disclosureToggle<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">></span></span><br /><span class="highlight-line"> Open disclosure</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>disclosureContent<span class="token punctuation">"</span></span> <span class="token attr-name">data-component</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>disclosureContent<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Disclosure content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Our tests now pass, so we're in the "Green" stage. I'd make a Git commit at this point. If there are any improvements we'd like to make, we can make them with confidence as long as our tests are passing, and commit again each time we're in a working state.</p>
<p>Let's write a failing test to begin interacting with our disclosure in <code>example.test.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token function">runTests</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></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"disclosure"</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></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"opens the disclosure on keyboard Enter press"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> docDisclosureToggle<span class="token punctuation">.</span><span class="token function">focus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token keyword">await</span> <span class="token function">sendKeys</span><span class="token punctuation">(</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> down<span class="token operator">:</span> <span class="token string">"Enter"</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureToggle<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-expanded"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span><span class="token function">equal</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token string">"true"</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token function">expect</span><span class="token punctuation">(</span>docDisclosureContent<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"hidden"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>to<span class="token punctuation">.</span>not<span class="token punctuation">.</span>exist<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// TODO: Reset the disclosure</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>With this test, we move focus to our toggle button, then use the <code>sendKeys</code> function from Web Test Runner commands to send a native keyboard <code>Enter</code> key press. We're checking that our toggle button gets updated attributes, and that our disclosure content is no longer hidden. We'd want similar tests for mouse click and the keyboard <code>Space</code> press, as well as testing that the disclosure closes on these interactions if it's already open.</p>
<p>We're in the "Red" stage again, but I'll leave the rest of the work to you to add the remaining tests and get them to pass. A cool thing is that the functionality needed for this component to work also creates some handy utility functions for our tests, such as resetting the disclosure at the end of each test to create a consistent starting point for other tests. And these utilities are easy to unit test with this approach as well.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>This workflow for test-driven HTML and accessibility provides so much value in my daily work, and I hope teams can adopt this technique to create a more responsible and usable web for everyone. I've used this setup extensively in large projects with complex UI patterns and it has scaled gracefully with several hundred tests running at all times.</p>
<p>With this setup, we can write unit tests that have access to the real DOM across major browsers, which allows us to check our HTML for validity, interactivity, and accessibility. We can run these tests in multiple browsers at once with every change, and preserve accessibility from the very start of our project. These expectations are permanently captured in our code, so we can freely refactor our work and add new features without introducing regressions.</p>
<p>The examples in this post assume a static HTML file that we directly linked our tests to. This is the fastest and most direct way to test our HTML, but we don't want our test file to load in production. As a result, we'd need to remove the <code><script></code> tag before deploying to the web. To make this approach more convenient, it'd be good to do this automatically with a build command that creates a <code>/dist</code> folder or something similar.</p>
<p>Most projects probably won't just have static HTML files, and instead render components with JavaScript. In this case, we create standalone <code>.test.js</code> files that import corresponding JavaScript modules and call render functions or methods directly. From there, the workflow and benefits are all the same, but there may be a little more effort in setting up specific UI situations to test vs. having HTML ready to go.</p>
<p>As a final consideration, this testing technique <em>enhances</em>, not <em>replaces</em>, other forms of accessibility testing and shifts a lot of the feedback earlier in the process. Using accessibility-focused code linters, automated accessibility testing tools, and manual accessibility review together provides the most value.</p>
<p>Many of the examples we covered in this post are checks we would otherwise have to make manually, either through code review or running accessibility audit tools. By writing unit tests for accessibility, we can discover and fix more issues as they arise. And we free more capacity to manually evaluate other aspects of accessibility, such as using assistive technology to evaluate the flow of a page or automated testing tools to check color contrast.</p>
<p>Automated tools cover about <a href="https://www.deque.com/automated-accessibility-testing-coverage/">30% of the WCAG success criteria and detect about 57% of overall issues</a>, leaving the rest for manual evaluation. With this approach, unit tests can increase coverage, particularly with interactive UI patterns. Even with more automated test coverage, manual review remains the most effective accessibility testing technique for finding issues and building empathy.</p>
<p>My long-term goal with this work is to build a robust, universal accessibility test collection that can be used across projects. As we explored with our disclosure example, it's also possible to create test suites for custom UI patterns such as accordions, menus, tooltips, and others.</p>
<p>Let me know if you'd be interested in using this test collection or if you'd like to collaborate. Together, we can make test-driven accessibility and accessibility-first development a reality.</p>
The hidden attribute in HTML
2023-12-11T00:00:00Z
https://htmhell.dev/adventcalendar/2023/11/
by Ahmad El-Alfy<br><p>The <code>hidden</code> attribute allows us to hide HTML elements from the page. When it was introduced, it worked in a very simple way: it set the CSS <code>display</code> property to <code>none</code>.</p>
<p><a href="https://twitter.com/getifyX/status/1131118316901326848">Many people voiced concerns</a> because here we are, mixing styles with markup again. To be fair, this was at a time before the rise of frameworks and the change in people's mindsets where separation of concern became a less debatable issue. <a href="https://meowni.ca/hidden.is.a.lie.html">There were also others</a> who opposed the attribute because it can be easily overridden by CSS. A solution to avoid hidden elements from showing unintentionally is adding this snippet to your stylesheet to ensure it overrides other rules used on the same element.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">[hidden]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none <span class="token important">!important</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>There are differing opinions on using the <code>!important</code> declaration, as some authors considered it code smell. Later, the use of <code>!important</code> in utility classes became more accepted.</p>
<div class="highlight u-mb"><p>A modern way to mitigate the use of <code>!important</code> is the use of CSS Cascade Layers. CSS layers is implemented by using the <code>@layer</code> directive, allowing us to define specific layers for our styles. This allow us to control the specificity and ordering of the CSS rules without resorting to <code>!important</code>. This approach promotes a more maintainable and scalable codebase, reducing the reliance on <code>!important</code> declarations and making it easier to manage styles across different components and elements. All we need to do is to add the previous snippet in the utility layer and moving that layer to be the last. This way we can ensure that their styles take precedence without the need for <code>!important</code>.</p></div>
<p>Let's take an example where we need to toggle the display of an element. We can do that by setting its <code>display</code> property to <code>none</code> using JavaScript and inline CSS. This is a common approach that many follow to hide an element instantly. To show the element again we can change the display back to whatever value we want.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token comment">// Hide the element</span></span><br /><span class="highlight-line">el<span class="token punctuation">.</span>style<span class="token punctuation">.</span>display <span class="token operator">=</span> <span class="token string">'none'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">// Show the element again by setting the display to another value</span></span><br /><span class="highlight-line">el<span class="token punctuation">.</span>style<span class="token punctuation">.</span>display <span class="token operator">=</span> <span class="token string">'block'</span><span class="token punctuation">;</span></span></code></pre>
<p>The problem with setting the display to something other than <code>none</code> is that we could be dealing with an element that has different display values according to the media size for example (e.g. <code>block</code>, <code>flex</code> ... etc). In that case, setting it to a specific value can introduce some troubles if the display types don't match.</p>
<p>Another approach would be to use the <code>removeProperty</code> method, available on the elements <code>style</code> property, which removes specific inline styles.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token comment">// Show the element again by removing the inline declared property</span></span><br /><span class="highlight-line">el<span class="token punctuation">.</span>style<span class="token punctuation">.</span><span class="token function">removeProperty</span><span class="token punctuation">(</span><span class="token string">'display'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>One argument in favor of the <code>hidden</code> attribute is its semantic value. It clearly indicates that an element using the attribute is intended to be hidden from display. Another argument is that we can hide content in HTML without needing CSS. So without having to set inline styles, we can add and remove the attribute on the element as we want.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token comment">// Hide the element</span></span><br /><span class="highlight-line">el<span class="token punctuation">.</span>hidden <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token comment">// We can also use addAttribute</span></span><br /><span class="highlight-line">el<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'hidden'</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">// Show the element</span></span><br /><span class="highlight-line">el<span class="token punctuation">.</span>hidden <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token comment">// We cal also use removeAttribute</span></span><br /><span class="highlight-line">el<span class="token punctuation">.</span><span class="token function">removeAttribute</span><span class="token punctuation">(</span><span class="token string">'hidden'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<h2 id="the-future-of-hidden-attribute">The future of <code>hidden</code> attribute</h2>
<p>Initially, the <code>hidden</code> attribute was considered a boolean attribute. If the attribute was present on an HTML element, it would be effective regardless of its value:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>This element will be hidden<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>This element is also hidden<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">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>This one is hidden as well<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <a href="https://github.com/whatwg/html/pull/7475">specification has changed recently</a>, and the <code>hidden</code> attribute is now an enumerated attribute. This means it can have specific named values. The new value introduced in the specification is <code>until-found</code>, which triggers a new state called the "Until Found" state.</p>
<p>If the <code>hidden</code> attribute is present without a value, or with any value other than <code>until-found</code>, it will behave as it did before. However, if the value is <code>until-found</code>, the element will be in the "Until Found" state. In this state, the element will be hidden until it is found by the user agent.</p>
<h2 id="the-hidden-until-found-state">The hidden until found state</h2>
<p>The hidden until found state was created to solve a common problem. Let's look at tabs and accordions for example. These UI components contain elements that are undiscoverable until a certain action is done (e.g clicking a button). This makes the content unavailable to screen readers, search engines, and the built-in find-in-page feature in browsers.</p>
<p>Unlike the standard "hidden" state, which completely hides the content by setting its <code>display</code> property to <code>none</code>, the "hidden until found" state uses the <code>content-visibility</code> property to hide content while still making it discoverable by search engines, find-in-page functionality, and linking. Consider the following example (currently only supported in Chrome and Edge).</p>
<iframe title="Hidden Attribute" scrolling="no" loading="lazy" width="100%" height="500" src="https://v17.livecodes.io/?x=id/rie7i3ccg28&embed=true">
See the project <a href="https://v17.livecodes.io/?x=id/rie7i3ccg28" target="_blank">Hidden Attribute</a> on <a href="https://livecodes.io/" target="_blank">LiveCodes</a>.
</iframe>
<p>In this example, try using the browser's search feature (find-in-page) and type the word "banana." After the first result is highlighted, attempting to navigate to the next result will reveal the "Introduction" section. Following that, the subsequent sections will become visible in sequence. Note that after an element is revealed, the attribute is removed keeping the element visible. Clicking the "Reset Display" button will hide the sections again, enabling you to retry the process. Additionally, clicking any of the links will automatically display the corresponding sections.</p>
<p>Note that this example doesn't rely on JavaScript, except for the functionality used to reset the display. Instead, the behavior is achieved purely with HTML. As the corresponding sections are "found," the hidden attribute is automatically removed from the elements, triggering their visibility.</p>
<h2 id="browser-support">Browser support</h2>
<p>Presently, this functionality is accessible in Chrome and Edge. While Firefox has expressed an <a href="https://github.com/mozilla/standards-positions/issues/612">intention</a> to incorporate it, there are <a href="https://lists.webkit.org/pipermail/webkit-dev/2022-March/032142.html">no indications</a> of similar developments from Safari yet. Currently, the feature is marked as "Experimental," indicating that its functionality may undergo changes in the future.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>The anticipated enhancements to the hidden attribute are poised to positively influence and enhance the user experience for those utilizing the find-in-page feature in browsers. The behavior showcased in <a href="https://twitter.com/JosephArhar/status/1567279577160126464">this demo</a> by Joey Arhar aligns with our expectations when searching for keywords that match content on a page, reinforcing the potential benefits of these improvements.</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://github.com/whatwg/html/pull/7475">The pull request that adds <code>hidden="until-found"</code> attribute</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden">Hidden attribute on MDN</a></li>
<li><a href="https://twitter.com/JosephArhar/status/1567279577160126464">Joey Arhar demo showing hidden until found on Wikipedia</a></li>
</ul>
<h2 id="special-thanks">Special thanks</h2>
<p>Many thanks to Saptak Sengupta and Eric Bailey for their invaluable reviews of this post; their insightful feedback greatly enhanced the final result. A special acknowledgment goes to Manuel Matuzovic for accepting my contribution to this year's Advent calendar and his patient support until I completed this blog. Thanks for offering a platform where we can share our thoughts and discussions about HTML.</p>
Template for accessibility guidelines
2023-12-10T00:00:00Z
https://htmhell.dev/adventcalendar/2023/10/
by Steve Frenzel<br><h2 id="foreword">Foreword</h2>
<p>This template is opinionated and intended as a starting point for those who want to define how accessibility is dealt with in their company. It does not matter whether your title is developer, designer, project manager or something else.</p>
<p>I created it based on my experience working with a very small start-up, a digital agency with more than 250 employees and a monolithic company with very fixed structures.</p>
<p>When creating this template, I had the <a href="https://webaim.org/projects/million/#wcag">six most common WCAG failures</a> in mind, but also what processes take place when you collaborate with other people. Nothing is set in stone and it should be adapted in consultation with your colleagues, because accessibility is a group effort! More on that in <a href="https://htmhell.dev/adventcalendar/2023/10/#1-prependix">Prependix</a>.</p>
<p>As a reference, I refer to the <a href="https://www.w3.org/TR/WCAG22/">Web Content Accessibility Guidelines (WCAG) 2.2</a>, as they are popular and well documented. However, you are free to include a different one or more in this template, depending on your location and the requirements that need to be met.</p>
<p>With all that being said, let's have a look at the template! š</p>
<h2 id="table-of-contents">Table of contents</h2>
<ol>
<li><a href="https://htmhell.dev/adventcalendar/2023/10/#1-prependix">Prependix</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/10/#2-design">Design</a><br />
2.1 <a href="https://htmhell.dev/adventcalendar/2023/10/#21-color-contrast">Color contrast</a><br />
2.2 <a href="https://htmhell.dev/adventcalendar/2023/10/#22-font-size">Font size</a><br />
2.3 <a href="https://htmhell.dev/adventcalendar/2023/10/#23-touch-target-size">Touch target size</a><br />
2.4 <a href="https://htmhell.dev/adventcalendar/2023/10/#24-conveying-meaning">Conveying meaning</a><br />
2.5 <a href="https://htmhell.dev/adventcalendar/2023/10/#25-focus-behaviour">Focus behaviour</a><br />
2.6 <a href="https://htmhell.dev/adventcalendar/2023/10/#26-media-controls">Media controls</a><br />
2.7 <a href="https://htmhell.dev/adventcalendar/2023/10/#27-user-flow">User flow</a><br />
2.8 <a href="https://htmhell.dev/adventcalendar/2023/10/#28-landmarks--annotations">Landmarks & annotations</a><br />
2.9 <a href="https://htmhell.dev/adventcalendar/2023/10/#29-handover">Handover</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/10/#3-development">Development</a><br />
3.1 <a href="https://htmhell.dev/adventcalendar/2023/10/#31-semantic-html">Semantic HTML</a><br />
3.2 <a href="https://htmhell.dev/adventcalendar/2023/10/#32-skip-link">Skip link</a><br />
3.3 <a href="https://htmhell.dev/adventcalendar/2023/10/#33-heading-levels">Heading levels</a><br />
3.4 <a href="https://htmhell.dev/adventcalendar/2023/10/#34-typography">Typography</a><br />
3.5 <a href="https://htmhell.dev/adventcalendar/2023/10/#35-buttons--anchors">Buttons & anchors</a><br />
3.6 <a href="https://htmhell.dev/adventcalendar/2023/10/#36-alternative-text">Alternative text</a><br />
3.7 <a href="https://htmhell.dev/adventcalendar/2023/10/#37-operability">Operability</a><br />
3.8 <a href="https://htmhell.dev/adventcalendar/2023/10/#38-preference-detection">Preference detection</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/10/#4-testing">Testing</a><br />
4.1 <a href="https://htmhell.dev/adventcalendar/2023/10/#41-browsers">Browsers</a><br />
4.2 <a href="https://htmhell.dev/adventcalendar/2023/10/#42-browser-extensions">Browser extensions</a><br />
4.3 <a href="https://htmhell.dev/adventcalendar/2023/10/#43-screen-reader">Screen reader</a><br />
4.4 <a href="https://htmhell.dev/adventcalendar/2023/10/#44-pipeline-integration">Pipeline integration</a><br />
4.5 <a href="https://htmhell.dev/adventcalendar/2023/10/#45-other-apps--tools">Other apps & tools</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2023/10/#5-appendix">Appendix</a></li>
</ol>
<h2 id="1-prependix">1. Prependix</h2>
<p>To ensure that our products are usable and accessible, we comply with <strong>level AA success criteria</strong> of the <a href="https://www.w3.org/TR/WCAG22/">Web Content Accessibility Guidelines (WCAG) 2.2</a>. For a better understanding of these guidelines, check out the following resources:</p>
<ul>
<li><a href="https://www.tempertemper.net/blog/wcag-but-in-language-i-can-understand">WCAG, but in language I can understand</a> by tempertemper</li>
<li><a href="https://intopia.digital/wp-content/uploads/2023/10/Intopia-WCAG-2.2-Map-Portrait-Mode.pdf">WCAG 2.2 Map</a> by Intopia</li>
<li><a href="https://not-checklist.intopia.digital/">Accessibility Not-Checklist</a> by Intopia</li>
</ul>
<p><strong>Accessibility is a group effort and not a one-person show.</strong> It goes beyond leaving it to designers or developers. Being aware and curious about it will not only help you, but also the people around you.</p>
<p><strong>It is not possible to create a product that is 100% accessible.</strong> However, this is no reason to give up or to not start at all. āProgress over perfectionā is our approach, because every little improvement counts and will make someoneās life easier.</p>
<p><strong>You're never done with accessibility.</strong> Wether you're just starting your journey, or maintaining a current project: People (and their needs) are constantly changing, as well as technology, so you'll need to pay attention and adapt accordingly. If you want to get a better understanding of this particular topic, check out <a href="https://inclusive.microsoft.design/">Microsoft Inclusive Design</a>.</p>
<h2 id="2-design">2. Design</h2>
<p>Whenever possible, we want to not only meet the minimum for level AA success criteria, but also go beyond that. It is therefore possible that there is no direct WCAG recommendation for some of our requirements.</p>
<h3 id="21-color-contrast">2.1 Color contrast</h3>
<a href="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-color-contrast-example.png">
<img alt="Illustration showcasing the difference of color contrast for an item on the Kaufland website. There are two images, both showing the contrast ratio. The first one has insufficient color contrast, showing values of 3.31:1 and 3.01:1. The second one is were requirements are met, with ratios of 4.6:1 and 4.65:1." src="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-color-contrast-example.png" />
</a>
<p><strong>Color contrast</strong> must be level AA, if possible level AAA compliant. If text is in front of an image, provide a layer in between.</p>
<p><em>Related WCAG recommendation: <a href="https://www.w3.org/TR/WCAG22/#contrast-minimum">Success Criterion 1.4.3 Contrast (Minimum)</a></em></p>
<h3 id="22-font-size">2.2 Font size</h3>
<a href="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-font-size-example.png">
<img alt="Illustration showcasing the difference of font sizes on the german Samsung website. Both images showing the font sizes. The first one's font size is too small, with only 12 pixel. The second one is meeting the requirement with 14 pixel." src="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-font-size-example.png" />
</a>
<p><strong>Font size</strong> is at least 14 pixels, preferably 16 pixels.</p>
<p><em>No related WCAG recommendation, this is based on real life experience.</em></p>
<h3 id="23-touch-target-size">2.3 Touch target size</h3>
<a href="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-touch-target-size-example.png">
<img alt="Illustration showcasing the difference of touch target sizes on the german Bosch website. Both images show a section of the footer, with three social media icons having a 2 pixel solid red broder around the, to visualise the target size. The first one's target size is too small with only 24 by 24 pixel (yet meeting the WCAG requirement). The second one's sufficient with 44 by 44 pixel." src="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-touch-target-size-example.png" />
</a>
<p><strong>Touch target size</strong> should be at least 44 x 44 pixels, preferably more.</p>
<p><em>Related WCAG recommendation: <a href="https://www.w3.org/TR/WCAG22/#target-size-enhanced">Success Criterion 2.5.5 Target Size (Enhanced)</a></em></p>
<h3 id="24-conveying-meaning">2.4 Conveying meaning</h3>
<a href="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-conveying-meaning-example.png">
<img alt="Illustration showcasing the difference of meaningfull labels on the german Mango website. Both images show a section of the main navigation. The first one's only showing four icons but without a label. The second one's having labels associated to the icons: Search, log in, wishlist and cart." src="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-conveying-meaning-example.png" />
</a>
<p><strong>Convey meaning</strong> not only through colors or icons. If possible, add a descriptive label to the element in question.</p>
<p><em>Related WCAG recommendation: <a href="https://www.w3.org/TR/WCAG22/#use-of-color">Success Criterion 1.4.1 Use of Color</a></em></p>
<h3 id="25-focus-behaviour">2.5 Focus behaviour</h3>
<a href="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-focus-behaviour-example.png">
<img alt="Illustration showcasing focus behaviour on the Rituals website. All the images show a section with a headline, some text and a 'Shop Now' button. The first image shows the button with no focus. The second image shows the focussed button, but it's dark blue focus is barely visible, as it's directly attached to the black button. The third image shows a black focus ring with 2 pixel distance on every side, making it much more visible." src="https://htmhell.dev/images/advent2023/accessibility-template/htmlhell-focus-behaviour-example.png" />
</a>
<p>All interactive elements have a <strong>clearly visible focus state</strong>. It might be a change of background and font color, or a focus ring. If the latter, it should have a width and offset of at least 2 pixels.</p>
<p><em>Related WCAG recommendations:</em></p>
<ul>
<li><a href="https://www.w3.org/TR/WCAG22/#focus-visible">Success Criterion 2.4.7 Focus Visible</a></li>
<li><a href="https://www.w3.org/TR/WCAG21/#non-text-contrast">Success Criterion 1.4.11 Non-text Contrast</a></li>
<li><a href="https://www.w3.org/TR/WCAG22/#focus-not-obscured-minimum">Success Criterion 2.4.11 Focus Not Obscured (Minimum)</a></li>
</ul>
<h3 id="26-media-controls">2.6 Media controls</h3>
<p>When creating components such as carousels (sometimes called sliders) or any sort of media player, make sure it has controls to:</p>
<ul>
<li>Navigate to the next or previous item</li>
<li>Stop and start the media</li>
<li>Show or hide captions (if applicable)</li>
</ul>
<p><em>Related WCAG recommendation:</em></p>
<ul>
<li><a href="https://www.w3.org/TR/WCAG22/#captions-prerecorded">Success Criterion 1.2.2 Captions (Prerecorded)</a></li>
<li><a href="https://www.w3.org/TR/WCAG22/#pause-stop-hide">Success Criterion 2.2.2 Pause, Stop, Hide</a></li>
</ul>
<h3 id="27-user-flow">2.7 User flow</h3>
<p>When visualising how users interact with a certain feature or component, keep in mind they will not only use a mouse. They might also use a keyboard, screen readers or other assistive technologies. So ask yourself: Can this (thing I'm working on) be used by anyone at any time? Is there something that might exclude certain people?</p>
<p><em>Related WCAG recommendation: <a href="https://www.w3.org/TR/WCAG22/#operable">Operable</a></em></p>
<h3 id="28-landmarks-and-annotations">2.8 Landmarks & annotations</h3>
<p>Provide a version of your design with <strong>landmarks and accessibility annotations</strong>. This will help communicating with developers and improve accessibility across all browsers. You can find plenty of resources regarding this topic (and much more) in Stephanie Walter's <a href="https://stephaniewalter.design/blog/a-designers-guide-to-documenting-accessibility-user-interactions/">Designerās Guide to Documenting Accessibility & User Interactions</a>.</p>
<h3 id="29-handover">2.9 Handover</h3>
<p>A design is only ready for development, if it has been <strong>approved by at least one other member of your team</strong> (Note by the author: This could be a developer, another designer, quality assurance, accessibility expert, or whoever else might be responsible). Before moving on, all open issues must be resolved.</p>
<p>(Note by the author: All things mentioned are supposed to help you avoid the most common accessibility mistakes but this list isn't extensive and there's much more to consider. Again, this template is supposed be a starting point. Feel free to extend it however you want!)</p>
<h2 id="3-development">3. Development</h2>
<p>Putting the following rules into practice will ensure a robust, easy-to-use UI which will not only improve accessibility, but in certain cases might also be benfecial for SEO.</p>
<h3 id="31-semantic-html">3.1 Semantic HTML</h3>
<p>Use <strong>semantic HTML</strong> wherever possible to leverage native browser behavior. If progressive enhancement is needed, apply it with CSS and / or JavaScript.</p>
<p><em>Related MDN documentation:</em> <a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement">Progressive enhancement</a></p>
<h3 id="32-skip-link">3.2 Skip link</h3>
<p>Add a <strong>skip link</strong> so keyboard users can jump straight to the main content. This also requires a <code><main></code> element present in the DOM. Related links:</p>
<ul>
<li><a href="https://webaim.org/techniques/skipnav/#creating">Creating "Skip Navigation" Links</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main">Documentation <code><main></code> element</a></li>
</ul>
<h3 id="33-heading-levels">3.3 Heading levels</h3>
<p><strong>Heading levels</strong> should be used appropriately and not be skipped. Also do not use heading elements simply for the sake of styling! If a heading needs to look a certain way, apply a CSS class instead.</p>
<p><em>Related MDN documentation:</em> <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements">The HTML Section Heading elements</a><br />
<em>Related WCAG recommendation: <a href="https://www.w3.org/TR/WCAG22/#section-headings">Success Criterion 2.4.10 Section Headings</a></em></p>
<h3 id="34-typography">3.4 Typography</h3>
<p>For <strong>typography</strong>, use a relative unit like <code>rem</code> instead of <code>px</code>. This will give users the ability to adjust the font size to their needs. Also install fonts locally instead of fetching them from an external source to improve performance.</p>
<p><em>Related article:</em> <a href="https://www.joshwcomeau.com/css/surprising-truth-about-pixels-and-accessibility/#accessibility-considerations-5">The Surprising Truth About Pixels and Accessibility</a></p>
<h3 id="35-buttons-and-anchors">3.5 Buttons & anchors</h3>
<p>Make sure you use the right element when deciding between <strong>a button and a link</strong> (actually called <strong>anchor</strong>). A button does something, an anchor element changes the URL in the browser. There are exceptions though, for example an anchor with the <code>email</code> or <code>tel</code> attribute. Both elements should have an accessible name for optimal screen reader support and search engine optimisation.</p>
<p><em>Related MDN documentation:</em></p>
<ul>
<li><a href="https://developer.mozilla.org/de/docs/Web/HTML/Element/button">Documentation <code>button</code> element</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a">Documentation <code>a</code> element</a></li>
</ul>
<p><em>Related article:</em></p>
<ul>
<li><a href="https://css-tricks.com/buttons-vs-links/">Button versus links</a></li>
</ul>
<h3 id="36-alternative-text">3.6 Alternative text</h3>
<p>Images must have <strong>descriptive alternative text</strong>. If they are purely decorative, use <code>alt=""</code>. Otherwise provide <a href="https://design102.blog.gov.uk/2022/01/14/whats-the-alternative-how-to-write-good-alt-text/">a sufficient image description</a>. If you must provide an icon-only interaction, make sure it has an accessible name by using <code>aria-label</code>.</p>
<p><em>Related WCAG recommendation: <a href="https://www.w3.org/TR/WCAG22/#non-text-content">Success Criterion 1.1.1 Non-text Content</a></em></p>
<h3 id="37-operability">3.7 Operability</h3>
<p>Interactive elements can be <strong>operated by mouse, keyboard, screen reader and other assistive technology</strong>. If such an element must consist only of an icon or color (which it should not), provide an accessible name with <code>aria-label</code>.</p>
<h3 id="38-preference-detection">3.8 Preference detection</h3>
<p>Some people have a <strong>preference for how content should be displayed</strong> on their devices. For example, they may have a dark theme activated on their operating system, or a high contrast mode. It is also possible that they prefer motion or transparency to be reduced. To respect all these scenarios, the following media queries can be applied:</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-color-scheme</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast">prefers-contrast</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion">prefers-reduced-motion</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-transparency">prefers-reduced-transparency</a></li>
</ul>
<h2 id="4-testing">4. Testing</h2>
<p>Whether you work in design, development or quality assurance: Testing is an essential part of the job and happens during the process and before you ship anything.</p>
<h3 id="41-browsers">4.1 Browsers</h3>
<p><strong>Use different browsers to test</strong> what you are working on. Google <strong>Chrome</strong> (Chromium engine) and Mozilla <strong>Firefox</strong> (Gecko engine) can be used on any operating system, except Apple iOS. When using MacOS, use Apple <strong>Safari</strong> (WebKit engine) as well.</p>
<h3 id="42-browser-extensions">4.2 Browser extensions</h3>
<p><strong>Browser extensions</strong> are a free and quick way to run accessibility scans. Most of them are based on the axe-core library and a mix of the following will cover enough ground. However, there are many more extensions out there worth exploring and testing.</p>
<ul>
<li><a href="https://www.deque.com/axe/devtools/">axe DevTools</a></li>
<li><a href="https://www.getstark.co/">Stark</a> (To generate reports)</li>
<li><a href="https://wave.webaim.org/">WAVE</a></li>
</ul>
<h3 id="43-screen-reader">4.3 Screen reader</h3>
<p>A <strong>screen reader</strong> will help you to understand the structure of a page or element. It will also show you a different way of user interaction. On <strong>Windows</strong> you can use <a href="https://www.freedomscientific.com/products/software/jaws/">JAWS</a> (paid) or <a href="https://www.nvaccess.org/download/">NVDA</a> (free). <a href="https://www.apple.com/accessibility/vision/">VoiceOver</a> is available on all <em>Apple</em> devices for free. For <strong>Android</strong> you can use <a href="https://support.google.com/accessibility/android/answer/6283677?hl=en">Talkback</a>. Related link: <a href="https://www.sarasoueidan.com/blog/testing-environment-setup/#guides-to-browsing-and-navigating-content-with-a-screen-reader">Guides to browsing and navigating content with a screen reader by Sara Soueidan</a></p>
<h3 id="44-pipeline-integration">4.4 Pipeline integration</h3>
<p>Integrating accessibility checks into the <strong>deployment pipeline</strong> is another great way to ensure requirements are met. For example a push or merge will not be possible if there are linting errors or a failed unit test. Related links:</p>
<ul>
<li><a href="https://www.adrianbolonio.com/blog/accessibility-github-actions">Automating the accessibility tests of your source code with GitHub Actions</a></li>
<li><a href="https://www.getstark.co/blog/introducing-stark-for-developers-beta">Stark for GitHub</a></li>
</ul>
<h3 id="45-other-apps-and-tools">4.5 Other apps & tools</h3>
<p>To check <strong>WCAG conformance</strong> of color contrast, use the app <a href="https://www.tpgi.com/color-contrast-checker/">Colour Contrast Analyser by TPGi</a>. You can also check the color contrast in the browser using <a href="https://webaim.org/resources/contrastchecker/">WebAim Contrast Checker</a>.</p>
<p>When using <strong>Figma</strong>, there are a couple of great plugins to check for accessibility. <strong>Stark</strong> has a great UI and the free version offers <strong>contrast check, adding landmarks and vision simulator</strong>. Another noteworthy plugin is <a href="https://www.figma.com/community/plugin/731310036968334777/A11y---Focus-Order">A11y - Focus Order</a>.</p>
<h2 id="5-appendix">5. Appendix</h2>
<p>This is a dynamic document and may be subject to change over time. If content or links are no longer up-to-date, please contact <em>[name of colleague]</em> via <em>[communication tool of your choice]</em> or reach out via e-mail at <em>[fake@email.com]</em>.</p>
What the slot?
2023-12-09T00:00:00Z
https://htmhell.dev/adventcalendar/2023/9/
by Egor Kloos (aka dutchcelt)<br><p>Web Components. The discussion seems to pop up more than it used to.</p>
<ul>
<li><a href="https://jakelazaroff.com/words/web-components-will-outlive-your-javascript-framework/">Web Components Will Outlive Your JavaScript Framework</a></li>
<li><a href="https://blog.jim-nielsen.com/2023/html-web-components-an-example/">HTML Web Components: An Example</a></li>
<li><a href="https://github.com/material-components/material-web/discussions/5004">Material web components 1.0 release</a></li>
<li><a href="https://miriam.codes/2023/11/15/components/">HTML Web Components are Just JavaScript?</a></li>
</ul>
<p>Web Components offer nifty features for creating components using JavaScript, CSS, and HTML. They have been available in all major web browsers since 2017. Thatās long enough to start using them in production.</p>
<p>Web Components consist of three things that are intended to work together.</p>
<ol>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements">Custom Elements</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM">Shadow DOM</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots">Templates and Slots</a></li>
</ol>
<p>The great thing is that you can use each of these on their own. For HTMHell and fun, letās take a closer look at Slots and the <code><slot></code> element.</p>
<h2 id="what-are-slots?">What are slots?</h2>
<p>For slots to do their thing, we need a Shadow DOM. MDN on what the Shadow DOM is:</p>
<blockquote>A set of JavaScript APIs for attaching an encapsulated "shadow" DOM tree to an element ā which is rendered separately from the main document DOM ā and controlling associated functionality. In this way, you can keep an element's features private, so they can be scripted and styled without the fear of collision with other parts of the document.</blockquote>
<p>Slots only exist in the Shadow DOM and are a way to get content from the document (i.e. a web page).<br />
We have, in effect, two types of DOM. A Light DOM (the document) and the Shadow DOM attached to a <a href="https://web.dev/articles/custom-elements-v1">Custom Element</a> or a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow">basic set of HTML elements</a>.</p>
<p>Yes, you can have a Shadow DOM associated with a <code><div></code>, a <code><section></code> element, or a <code><main></code>!<br />
Slots are rather magical and can prove very useful in bridging these two DOMs.<br />
The best way to explain what a slot is is to show you. This basic example uses slots and a <code><div></code> element.</p>
<p class="codepen" data-height="420" data-default-tab="js,result" data-slug-hash="QWYORLN" data-user="dutchcelt" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/dutchcelt/pen/QWYORLN">
Attach a Shadow DOM to a DIV</a> by Egor Kloos (<a href="https://codepen.io/dutchcelt">@dutchcelt</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<h3 id="here-is-another-example">Here is another example</h3>
<p>Letās say you wanted to use a <code><slot></code> with a Custom Element and a <code><template></code>. For brevityās sake, I only show the two HTML parts, not the entire Custom Element setup.</p>
<h4>Custom element markup</h4>
<p>This is the HTML you would write on your web page.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>card-component</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">slot</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>A heading passed to the named slot 'heading'.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>blockquote</span><span class="token punctuation">></span></span>This will be passed on to the unnamed slot. Lorem ipsum dolor...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>blockquote</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>card-component</span><span class="token punctuation">></span></span></span></code></pre>
<h4>Template for your Shadow DOM</h4>
<p>The Template is āappendedā to the Shadow DOM inside the Custom Element, and the <code><slot></code> pulls the content in. The Custom Element now renders the template with the content from the page.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>linkheader<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>slot</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>slot</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>slot</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>slot</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></span></code></pre>
<p>The key is that you can place your <code><slot></code> into a more elaborate markup provided by the template.</p>
<h3 id="the-crispy-bits">The crispy bits</h3>
<p>Here is a short list of what makes slots great:</p>
<ul>
<li>Content inside the Shadow DOM is <a href="https://alice.pages.igalia.com/blog/how-shadow-dom-and-accessibility-are-in-conflict/">not always as accessible</a> as on a normal web page. Slots allow your content to exist in both. The content remains on the page, and the Shadow DOM can access and enhance it.</li>
<li>Slots can be placed in any order. Especially with named slots, as we can see in the Codepen above. In the document source, the āheadingā is the first element but displayed as the second element through the template.</li>
<li>It is easy for machines to index (i.e. search engines and crawlers like chatGPT) because the content is still on the web page. <em>Note: Most search engines now index the Shadow DOMās content.</em></li>
<li>Content changes on the page are passed on to the Shadow DOM. Slots are live; Ummm, I mean reactive!</li>
<li>You donāt need to style the slotted elements as the web page styling already styles them. Styles are passed along with the content.</li>
</ul>
<p>This all sounds very tasty. But before we choke on an unclosed <code><article></code> letās see whatās whiffy about slots.</p>
<h3 id="the-soggy-bits">The soggy bits</h3>
<p>This part requires a bit more explanation of the behaviour of Shadow DOM. Namely, itās Encapsulation.<br />
CSS-Tricks has an introduction to Web Components and contains a useful part about <a href="https://css-tricks.com/an-introduction-to-web-components/#aa-shadow-dom">Shadow Dom</a></p>
<blockquote>⦠the shadow DOM works sort of like an <code><iframe></code> where the content is cut off from the rest of the document; however, when we create a shadow root, we still have total control over that part of our page, but scoped to a context.</blockquote>
<p>For me, this is the killer feature of Web Components. Nothing goes in or outāscript contexts, IDs, styles, etc. Everything is nicely contained and controlled.<br />
But wait, isnāt one of the positives of slotted elements that they <s>smuggle</s> bring their given styles from the document <strong>with them</strong> to the Shadow DOM? Houston! We have a problem!</p>
<p>So maybe we donāt want those pesky styles from the document. But hey, at least we can just override them in the Shadow DOM. Right!?</p>
<p>Yes, we can! ⦠Sometimes. ā Note to reader: Feel free to insert random expletives ā</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted">Slotted elements</a> have weak specificity by design. Let me demonstrate with yet another Codepen.</p>
<p class="codepen" data-height="420" data-default-tab="html,result" data-slug-hash="VwgQYjb" data-user="dutchcelt" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/dutchcelt/pen/VwgQYjb">
Slotted elements are weak</a> by Egor Kloos (<a href="https://codepen.io/dutchcelt">@dutchcelt</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>In this example, we canāt style the slotted element. This is because the document already applies a style to them. (See the CSS tab in Codepen)<br />
We can force it to work when we add an <code>!important</code> to the <code>::slotted</code> rule. Even if the document style also adds an <code>!important</code> the <code>::slotted</code> style still holds up.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* Light DOM style */</span></span><br /><span class="highlight-line"><span class="token selector">h2</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> green <span class="token important">!important</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* Shadow DOM slotted styling */</span></span><br /><span class="highlight-line"><span class="token selector">::slotted(h2)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> navy <span class="token important">!important</span><span class="token punctuation">;</span> <span class="token comment">/* The colour is navy */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Well, thatās great, I mean GROSS!<br />
It's not as bad as you might think as this is a use-case where you do want to reverse the cascade using <code>!important</code>.</p>
<p>However, this means that styling slotted content from within the Shadow DOM always requires <code>!important</code> for each property. Remember that the initial style could matter so be very careful, especially if you don't have control over the content in the document.<br />
For most situations, you should accept that slotted elements keep the styling they're given. Only override when you really need to.</p>
<h3 id="but-what-if-i-want-to-have-my-cake-and-eat-it?">But what if I want to have my cake and eat it?</h3>
<p>We effectively only have 1 way to override the style of slotted content for the Shadow DOM in a clean and inclusive way. Select both the shadowed element and the slotted elements from within the document and style it from there.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.card-demo h2</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> navy<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>I confess adding these additional styles to the document is a bit weird. However, bundling these with the main styles is fine in most situations. This also allows document authors to explicitly apply an override when they need to.<br />
Before you do this, you should ask yourself again if you need these additional overrides. You may find, with some consideration, that you can make do without the added complexity.</p>
<h4>An alternative</h4>
<p>I have a work-a-round specific to my use case with Design System component libraries using Web Components. In this scenario, I have no control nor insight into the web page the library components are loaded. So I sometimes add some extra CSS to strengthen the slotted elements.</p>
<p>Because the component is explicitly being loaded anyway, I can directly insert <a href="https://web.dev/articles/constructable-stylesheets">Constructable Stylesheets</a> to the documentāno need to require a separate instruction to the developer to load additional styling. The Web Component can do it for you.</p>
<p>This Codepen demonstrates how you can <a href="https://codepen.io/dutchcelt/pen/JjxpdKp">add overrides</a> to the document using <code>adoptedStyleSheets</code>.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token comment">// Overriding the document styling</span></span><br /><span class="highlight-line"><span class="token keyword">const</span> sheet <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CSSStyleSheet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br />sheet<span class="token punctuation">.</span><span class="token function">replaceSync</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><br /><span class="highlight-line"> #demodiv {</span><br /><span class="highlight-line"> & h2 {</span><br /><span class="highlight-line"> color: navy;</span><br /><span class="highlight-line"> }</span><br /><span class="highlight-line"> & p {</span><br /><span class="highlight-line"> color: purple;</span><br /><span class="highlight-line"> }</span><br /><span class="highlight-line"> & [slot="subheading"] {</span><br /><span class="highlight-line"> color: steelBlue;</span><br /><span class="highlight-line"> font-size: 1.3333rem;</span><br /><span class="highlight-line"> font-weight: 300;</span><br /><span class="highlight-line"> }</span><br /><span class="highlight-line"> }</span><br /></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="highlight-line">document<span class="token punctuation">.</span>adoptedStyleSheets<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>sheet<span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>This is rather extreme, and I wouldn't necessarily recommend it. However, it is an interesting approach using new techniques like Constructable Stylesheets.</p>
<p>Slots, I love them, and I hate them. I also wouldn't want to go without them.</p>
<p>Regardless if youāll use Shadow DOM in the future, I hope slots are now a little less of a mystery.</p>
<p><strong>Update</strong> (December 10th, 2023):<br />
Initially the described behaviour of <code>!important</code> was incorrect and has been corrected.</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
The hidden depths of the input element
2023-12-08T00:00:00Z
https://htmhell.dev/adventcalendar/2023/8/
by Phil Nash<br><p>The <code><input></code> element is the most fascinating element in HTML.</p>
<p>Most elements behave the same way regardless of their attributes, the <code>type</code> attribute of the <code><input></code> element can take 1 of 22 different values that gives it not only different behaviour, but a different visual appearance too (many of which are hell to style, of course).</p>
<p>The <code><input></code> element is responsible for everything from text input, through checkboxes and radio buttons, to a button that resets all other elements in the form. I want to look beyond the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types">various types</a> that the <code><input></code> can embody, to the attributes you may not know about. Attributes that tweak the behaviour of the <code><input></code> to make it more usable, more accessible, and more applicable to various situations. Let's dig in.</p>
<h2 id="keyboard-control">Keyboard control</h2>
<p>On a mobile device, focusing a text input will bring up the default virtual keyboard. But what if the input you are expecting is numerical or some other format? In this case the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#inputmode"><code>inputmode</code> attribute</a> can help to optimise the keyboard for the input you are expecting.</p>
<p>If the expected input is an email address, you can set the <code>inputmode</code> to āemailā and the <code>@</code> symbol will surface on the main keyboard. If, however, you're looking for a number, then you can set the <code>inputmode</code> to ānumericā or ādecimalā and the number keypad will appear. This is useful in cases where your input is a number, but the input <code>type</code> of <code>number</code> isn't appropriate, for example with a one time password or a credit card number.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">inputmode</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>numeric<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>The full list of <code>inputmode</code> values that you can use are listed below with their keyboard appearance on iOS. Android will display different variations.</p>
<ul>
<li><code>none</code>: stops virtual keyoards appearing for when you want to implement your own keyboard (you probably don't want this)</li>
<li><code>text</code>: the default</li>
<li><code>numeric</code>: just numbers</li>
<li><code>decimal</code>: numbers and a decimal point</li>
<li><code>tel</code>: numbers and other dial pad characters</li>
<li><code>url</code>: the normal keyboard, with a period, forward slash and ".com" in place of the space bar</li>
<li><code>email</code>: the normal keyboard, with a small space bar, an "@" symbol and a period</li>
<li><code>search</code>: the normal keyboard, with a slightly smaller space bar, a period and a highlighted button that says "go" rather than the normal "return"</li>
</ul>
<p><img src="https://htmhell.dev/images/advent2023/inputmode.webp" alt="An animation showing a browser on iOS tabbing through the different inputmodes, appearing as listed above." /></p>
<p><code>inputmode</code> is also supported on <code><textarea></code>s and any element in <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#contenteditable"><code>contenteditable</code> mode</a>.</p>
<p>Get the right keyboard and your users will thank you as they input their data.</p>
<h2 id="inactive-inputs">Inactive inputs</h2>
<p>If you need to display an <code><input></code> element, but you don't want a user to be able to change the contents or you intend to change the contents in some other programmatic way, you might think that enabling the <code>disabled</code> attribute will help. However, disabled input elements are removed from the accessibility tree and thus disappear for users of assistive technologies. The contents of disabled inputs are also not submitted with the form.</p>
<p>An alternative to <code>disabled</code> is the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly"><code>readonly</code> attribute</a>. It makes the field inert, but the content remains accessible and will be submitted with the rest of the inputs within the form.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>The label<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>You can<span class="token punctuation">'</span>t change this<span class="token punctuation">"</span></span> <span class="token attr-name">readonly</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>input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<div class="highlight u-mb">
<p><strong>Note by Manuel</strong>: Not all screen readers announce the field as āread onlyā. You may want to test it first and decide whether to support it with an additional description.</p>
</div>
<p>You can also target <code>readonly</code> elements in CSS with the pseudoclass <code>:read-only</code>. Elements that can be read and written can be targeted with the <code>:read-write</code> pseudoclass.</p>
<p><code>readonly</code> only works on <code><input></code>s that behave like text controls and is also supported on <code><textarea></code> elements.</p>
<p>Choosing whether to use <code>readonly</code> over <code>disabled</code> on an element is about choosing whether an element is usable on the page. Don't forget to consider this.</p>
<h2 id="lights-camera-action">Lights, camera, action</h2>
<p>You might think the only way to have a user access the camera on their mobile device and take pictures or video is via JavaScript and the <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia"><code>getUserMedia</code> API</a>. But camera access is also available declaratively. On a file upload <code><input></code> you can specify the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/capture"><code>capture</code> attribute</a>, which will direct the element to trigger the device camera.</p>
<p>Sadly this is a feature that is <a href="https://caniuse.com/html-media-capture">limited to the mobile versions of supporting browsers</a>, on a desktop it will work like a regular file upload field.</p>
<p>For devices with front and back cameras, you can even provide hints for which camera to use. So if you are asking your user to take a selfie, you can set <code>capture="user"</code> to prefer the front-facing camera. If you are more interested in pictures of the surroundings, you can set <code>capture="environment"</code> to hint that you'd prefer the rear facing camera. The device can override these hints if, for example, it doesn't have a user facing camera available.</p>
<p>You can also use the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept"><code>accept</code> attribute</a> to hint at the type of content you want from the camera. If you accept image types, then the camera will be primed to take pictures, but if you hint that you want video then the camera will set up to record instead.</p>
<p>So, using the following HTML will produce the result below.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cheeeeese<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>file<span class="token punctuation">"</span></span> <span class="token attr-name">capture</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>user<span class="token punctuation">"</span></span> <span class="token attr-name">accept</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/jpeg,image/png<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>input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p><img src="https://htmhell.dev/images/advent2023/capture.webp" alt="An animation showing a file input on a page using the capture attribute set to "user" and the accept attribute set to image types. When touching on the input the camera is opened, facing the user with only photo as an option. An image is captured and it appears as a small thumbnail in the file input." /></p>
<p>This is a great way to capture an avatar for a user with no need for JavaScript, just HTML.</p>
<h2 id="spellbound">Spellbound</h2>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck"><code>spellcheck</code> attribute</a> is a generic attribute that can be used on any element. It's used to trigger the browser's spellchecking capability on elements where you also set <code>contenteditable="true"</code>. It also works on <code><input></code> elements and is definitely something you should consider.</p>
<p>Different browsers treat <code><input></code> elements differently. Firefox and Safari on the desktop will not spellcheck <code><input></code> elements without the attribute, but Chrome and Safari on iOS will. To get consistent behaviour across browsers, you should set the <code>spellcheck</code> attribute to "true" or "false" depending on the contents of the <code><input></code>.</p>
<p>For regular text, help your users out with <code>spellcheck="true"</code>. For identifiers, like usernames, email addresses, URLs, or other things that you would not expect to find in a dictionary, set <code>spellcheck="false"</code>.</p>
<p>One other thing to consider, how spellchecking is implemented is left to the browser. <a href="https://www.otto-js.com/news/article/chrome-and-edge-enhanced-spellcheck-features-expose-pii-even-your-passwords">Chrome and Edge have been known to send the contents of <code><input></code> elements to a spellchecking service over the network</a>, exposing whatever was in the field. If your <code><input></code> elements are capturing sensitive data, like personally identifiable information, like names or birth dates, or passwords, then you should set <code>spellcheck="false"</code> to avoid sharing it.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">spellcheck</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 attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>Note that a <code><input></code> with <code>type</code> of "password" won't leak its value, but if you implement a show password feature that sets the type to "text" then this opens the field to be spellchecked.</p>
<p>Getting your spellcheck settings consistent helps your users as they input data into your forms and being aware of the risks of spellcheck helps you protect your users' data.</p>
<h2 id="automatic-shift">Automatic Shift</h2>
<p>The default behaviour for an <code><input></code> on a mobile device is to capitalise the first word and make all other characters lowercase. This is fine when writing a sentence, however things like names and addresses expect all words to be capitalised. Other inputs, like airline ticket identifiers and UK postcodes expect their input to be in all capitals.</p>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize"><code>autocapitalize</code> attribute</a> is your friend in this case and saves your users having to use the shift key to produce text in the format you want it.</p>
<p>Capitalise the first letter of every word with <code>autocapitalize="words"</code> and capitalise every letter with <code>autocapitalize="characters"</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>full-name<span class="token punctuation">"</span></span> <span class="token attr-name">autocapitalize</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>words<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>input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>One thing to note, the <code>autocapitalize</code> attribute does not have an effect where it doesn't make sense, like on <code><input></code> elements with <code>type</code> of "email", "url", and, most importantly, "password".</p>
<p>Getting autocapitalisation right will make entering data on mobile devices much easier for your users.</p>
<h2 id="less-typing-is-always-better">Less typing is always better</h2>
<p>Last but not least, my absolute favourite <code><input></code> attribute is the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete"><code>autocomplete</code> attribute</a>.</p>
<p>It provides hints for the browser to fill in input fields with data it already knows. The <code>autocomplete</code> attribute can take <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values">an enormous number of values</a> that associate an <code><input></code> with many things the browser knows or can find out.</p>
<p>For things like registration or logging in, you can set <code>autocomplete</code> to values like "username" or "email". For passwords, you can use the value "new-password" to prevent a browser from autocompleting an existing password and to suggest a secure new password. Or you can use the value "current-password" to indicate that the browser or password manager should autocomplete the field with the user's password.</p>
<p>You can autocomplete a user's address with values like "street-address", "country-name" and "postal-code". For ecommerce applications you can combine the values with additional identifiers to separate between "billing" and "shipping" addresses.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Address<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>street-address shipping<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>street-address<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>input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>For one time passwords sent over SMS, you can use the value "one-time-password" to indicate that the browser should look at recently received messages and extract the OTP for autocomplete.</p>
<p>And if you're implementing passkeys, you can use the <code>autocomplete</code> value "webauthn" to help implement <a href="https://web.dev/articles/passkey-form-autofill">passkey autofill</a>.</p>
<p>Correctly using the <code>autocomplete</code> attribute is even a <a href="https://www.w3.org/WAI/WCAG22/Techniques/html/H98">sufficient technique</a> to fulfill <a href="https://www.w3.org/WAI/WCAG22/Understanding/identify-input-purpose">the WCAG level AA rule "identify input purpose"</a>. Providing fine-grained hints to the browser for the type of content the input fields are expecting means that they can be filled in saving time and typing for all users.</p>
<p>Conversely, there are times you might want to disable autocomplete entirely. Using <code>autocomplete="off"</code> will stop the browser remembering what was entered in an input. This is useful for fields that expect different input each time, like a text CAPTCHA.</p>
<p>Getting the right autocomplete attributes helps all users fill in inputs with the least amount of effort.</p>
<h2 id="picking-the-right-attributes">Picking the right attributes</h2>
<p>Designing and developing forms that work for all users is hard. The <code><input></code> element has many options that you can choose to use to make your forms more usable and accessible.</p>
<p>For controlling the available virtual keyboards use <code>inputmode</code>. To control whether an <code><input></code> can be updated, but also still read and submitted, choose <code>readonly</code> over <code>disabled</code>. You can trigger the camera on mobile devices using <code>capture</code>. Use the <code>spellcheck</code> attribtue to control whether spellchecking is activated on an input, and remember to set it to "false" on sensitive inputs. Save users time by using <code>autocapitalize</code> to control the capitalisation of text in an input. And finally, use <code>autocomplete</code> to help the browser fill in the contents of an input and save your users typing.</p>
<p>All of these attributes, when deployed correctly will help your users fill in your forms with the least amount of effort.</p>
Security Headers using <meta>
2023-12-07T00:00:00Z
https://htmhell.dev/adventcalendar/2023/7/
by Saptak S<br><p>Various <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers">HTTP headers</a> are sent between the user and the server of a website in the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Session">request-response cycle</a>. Some of these HTTP response headers sent by the server to the browser help enhance the security and privacy of the website's users. These sets of headers are often commonly known as <a href="https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html">security headers</a>. These can range anywhere from forcing the browser to always visit your website through <code>https</code> (<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security">Strict-Transport-Security</a>) to preventing <a href="https://owasp.org/www-community/attacks/xss/">Cross Site Scripting</a> and <a href="https://owasp.org/www-community/attacks/Clickjacking">Clickjacking</a> attacks on the website's user. You can check how well the security headers are set for your website using <a href="https://securityheaders.com/">securityheaders.com</a>.</p>
<p>These security headers (or any HTTP response headers) are usually set through the server configuration, i.e., nginx config, Apache config, or similar. However, in certain scenarios, like hosting a static website using GitHub Pages, the developers don't have a lot of control over the HTTP response headers. In such scenarios, the ability to add security headers declaratively through HTML itself can be helpful. The <code><meta></code> HTML tag helps in setting up two such security headers - <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy">Referrer Policy</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">Content Security Policy</a>.</p>
<h2 id="referrer-policy-using-lessmeta-namegreater">Referrer Policy using <code><meta name></code></h2>
<p>One of the most common ways a <code><meta></code> HTML tag is used is with a <code>name</code> and <code>content</code> attribute. The HTML standard allows the <code><meta></code> element to have <a href="https://html.spec.whatwg.org/multipage/semantics.html#meta-referrer"><code>name="referrer"</code></a> that gets delivered as a <code>Referrer-Policy</code> header. The <code>content</code> attribute defines the value of the <code>Referrer-Policy</code> header. For example,</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>referrer<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no-referrer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>will create the HTTP response header: <code>Referrer-Policy: no-referrer</code>. This will ensure that any visitor of your website, if they click on a link in your website, the <a href="https://datatracker.ietf.org/doc/html/rfc7231#section-5.5.2"><code>Referer</code></a> information of the visitor is not shared with the link. So the visited link will not know which previous URL the user has clicked the link from and helps protect the privacy of the user. There are a few different <a href="https://www.w3.org/TR/referrer-policy/#referrer-policies">Referrer-Policy values</a> that allow varied levels of information to be shared in the <code>Referer</code> header to the navigating link or subresources.</p>
<p>There are a few other ways that HTML allows setting up Referrer-Policy on individual links -</p>
<ul>
<li><code>referrerpolicy</code> attribute</li>
<li><code>rel="noreferrer"</code>.</li>
</ul>
<p>The <code>referrerpolicy</code> attribute can be used like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>http://example.com<span class="token punctuation">"</span></span> <span class="token attr-name">referrerpolicy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>origin<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>This means when the user clicks on this particular anchor tag, only the <code>origin</code> information of the current website is sent to <code>https://example.com</code>. The <code>referrerpolicy</code> attribute can also be set on elements like <code><img></code> or <code><iframe></code> to control their Referrer-Policy.</p>
<p>Adding <code>rel="noreferrer"</code> to an anchor tag ensures that no referrer information of the current webpage is sent to the visiting webpage.</p>
<h2 id="content-security-policy-using-http-equiv">Content Security Policy using <code>http-equiv</code></h2>
<p>Another way of using <code><meta></code> tags is using the <a href="https://html.spec.whatwg.org/multipage/semantics.html#pragma-directives"><code>http-equiv</code></a> attribute instead of the <code>name</code> attribute. The <code>http-equiv</code> attribute is used by servers to create various HTTP response headers. One such header is the <a href="https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-content-security-policy"><code>Content-Security-Policy</code></a>. Content Security Policy (CSP) is used to determine whether a content being loaded in the page is from a trusted source or not. CSP is versatile in controlling all different kinds of content from which script gets loaded to what a form target can be set to through its various <a href="https://w3c.github.io/webappsec-csp/#csp-directives">directives</a>, and hence protects your users from a wide range of attacks such as clickjacking attacks and cross-site scripting attacks.</p>
<p>The way to add a CSP using <code>http-equiv</code> is:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Content-Security-Policy<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>script-src <span class="token punctuation">'</span>self<span class="token punctuation">'</span>; form-action <span class="token punctuation">'</span>none<span class="token punctuation">'</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>The above line creates an equivalent HTTP response header</p>
<pre class="language-json"><code class="language-json"><span class="highlight-line">Content-Security-Policy<span class="token operator">:</span> <span class="token string">"script-src 'self'; form-action 'none';"</span></span></code></pre>
<p>The <code>script-src</code> directive in the above example ensures that URLs loaded in <code><script></code> element are from the same origin as the current webpage, and disallows any inline-scripts or script URLs from other websites. The <code>form-action</code> directive ensures that no form submission is allowed from the current webpage. These both help in preventing cross-site scripting attacks.</p>
<p class="highlight">
<strong>Caution:</strong> One very important thing to note is using this method, <code>report-uri</code>, <code>frame-ancestors</code>, and <code>sandbox</code> <a href="https://w3c.github.io/webappsec-csp/#directives">directives</a> cannot be set for a CSP. They are <a href="https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-content-security-policy">removed from the policy</a> before the policy is enforced if they are added using <code><meta></code> tag.
</p>
<h2 id="invalid-security-headers-in-lessmetagreater">Invalid security headers in <code><meta></code></h2>
<p>One of the issues with adding security headers using <code><meta></code> tag is developers often assume that any of the HTTP security response headers can be added through this method. This is not true and can often lead to a false sense of security since any invalid security header will be ignored by the browser. It is important to remember that even though <code>http-equiv</code> helps in defining HTTP response headers, it is an <a href="https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#enumerated-attribute">enumerated attribute</a>. Hence, it can only help in delivering a fixed set of <a href="https://html.spec.whatwg.org/multipage/semantics.html#pragma-directives">HTTP header</a> and any other HTTP header that is set using this method will be ignored.</p>
<p><img src="https://htmhell.dev/images/advent2023/security-header-meta-usage.png" alt="Table showing security headers used in meta tags as per Web Almanac 2022 security chapter. 3.11% websites in desktop and 2.60% websites in mobile use meta tag to set Referrer-Policy. 0.55% websites in desktop and 0.47% websites in mobile use meta tag to set CSP. 0.08% websites in desktop and 0.06% websites in mobile use meta tag to set not allowed security headers." /></p>
<p>In <a href="https://almanac.httparchive.org/en/2022/security#preventing-attacks-using-meta">Web Almanac 2022</a>, it was seen that 0.08% of the websites visited over the desktop had an invalid security header. So in conclusion, delivering Referrer-Policy and Content-Security-Policy security headers using the <code><meta></code> element can be really useful and protect your user's security and privacy, but it is also important to be aware of the limitations of this method.</p>
<h2 id="further-reading">Further Reading:</h2>
<ul>
<li><a href="https://almanac.httparchive.org/en/2022/security#preventing-attacks-using-meta">Web Almanac 2022 Security: Preventing attacks using <meta></a></li>
<li><a href="https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html">HTTP Security Response Headers Cheat Sheet</a></li>
<li><a href="https://html.spec.whatwg.org/multipage/semantics.html#pragma-directives">HTML spec: 4.2.5.3 Pragma directives</a></li>
<li><a href="https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html">Content Security Policy Cheat Sheet</a></li>
<li><a href="https://web.dev/articles/referrer-best-practices">Referer and Referrer-Policy best practices</a></li>
</ul>
Web Components FTW!
2023-12-06T00:00:00Z
https://htmhell.dev/adventcalendar/2023/6/
by Chris Ferdinandi<br><p>Web Components are a collection of technologies that you can use to create reusable custom elements, with built-in interactivity, automatically scoped (or encapsulated) from the rest of your code.</p>
<p>They have a wide range of features and functionality (some good, some bad, some ugly), but today, we're going to look at how to create your first Web Component using the most cliche of examples: the counter button.</p>
<p>We'll learn how this...</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>counter-button</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>counter-button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Automagically becomes somethings like this under-the-hood...</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">increase</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Clicked 0 Times<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"> <span class="token keyword">let</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token keyword">function</span> <span class="token function">increase</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> count<span class="token operator">++</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Clicked </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>count<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> Times</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>Let's dig in!</p>
<h2 id="creating-a-custom-element">Creating a custom element</h2>
<p>First, we'll create a custom element.</p>
<p>You can name it anything you want, but it must include at least one dash (<code>-</code>). Single-word web components are not allowed (those are reserved for native elements).</p>
<p>Let's uncreatively name ours <code>counter-button</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>counter-button</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>counter-button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Next, we need to register our custom element with JavaScript.</p>
<p>We do that with <a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define">the <code>customElements.define()</code> method</a>. We'll pass in the name of our custom element as the first argument, and a Class that extends <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement">the <code>HTMLElement</code> API</a> as our second.</p>
<p>When our page loads, this will automatically find any custom elements with the name we provided and instantiate a new instance of our Class on them.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'counter-button'</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// Stuff will happen here!</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>Inside the class, we'll add <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor">the <code>constructor()</code> method</a>. This runs when a new instance of the Class is created.</p>
<p>In it, we'll user <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super">the <code>super()</code> operator</a> to make sure we have access to the parent class (in this case, the <code>HTMLElement</code> object) properties.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'counter-button'</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /> <span class="token comment">/**<br /><span class="highlight-line"> * The class constructor object</span><br /> */</span><br /><span class="highlight-line"> <span class="token function">constructor</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Always call super first in constructor</span></span><br /><span class="highlight-line"> <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>Next, we'll add a property to track the number of clicks: <code>this.count</code>. You can think of this like Component state in your favorite JS library or framework.</p>
<p>(<em>In a JavaScript Class, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this">the <code>this</code> keyword</a> refers to the current instance of the Class.</em>)</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'counter-button'</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /> <span class="token comment">/**<br /><span class="highlight-line"> * The class constructor object</span><br /> */</span><br /><span class="highlight-line"> <span class="token function">constructor</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Always call super first in constructor</span></span><br /><span class="highlight-line"> <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Track the count</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>Finally, we'll use the <code>innerHTML</code> property to render a <code>button</code> into the custom element (<code>this</code>), with our <code>count</code> state displayed as part of the text.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'counter-button'</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /> <span class="token comment">/**<br /><span class="highlight-line"> * The class constructor object</span><br /> */</span><br /><span class="highlight-line"> <span class="token function">constructor</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Always call super first in constructor</span></span><br /><span class="highlight-line"> <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Track the count</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Render HTML</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>innerHTML <span class="token operator">=</span></span><br /><span class="highlight-line"> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><button>Clicked </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> Times</button></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>Now, a <code><button></code> is automatically rendered into the UI wherever we include our custom element. It looks like this in the HTML.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>counter-button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Clicked 0 Times<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>counter-button</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="adding-interactivity">Adding interactivity</h2>
<p>Web Components have <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#using_the_lifecycle_callbacks">"lifecycle functions"</a> that run when various things happen.</p>
<ul>
<li>The <code>constructor()</code> method is run when the element is created, before its injected into the UI.</li>
<li>The <code>connectedCallback()</code> method is run when the element is injected into the DOM, and again whenever it's moved or appended elsewhere.</li>
<li>The <code>disconnectedCallback()</code> method is run whenever the element is removed from the DOM.</li>
</ul>
<p>When the <code><button></code> is clicked, we want to increase our <code>count</code> by <code>1</code> and update the UI.</p>
<p>We don't need to listen for clicks until the element is loaded into the DOM, so we'll use <code>connectedCallback()</code> method to add a <code>click</code> event listener.</p>
<p>Inside our Web Component Class, <code>this</code> is the custom element (<code><counter-button></code>).</p>
<p>We'll attach a <code>click</code> event listener to <code>this</code>. Inside the event handler function, we'll increase our <code>count</code> by <code>1</code>, then render new text into the <code>firstElementChild</code>, the <code><button></code> element.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'counter-button'</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /> <span class="token comment">/**<br /><span class="highlight-line"> * The class constructor object</span><br /> */</span><br /><span class="highlight-line"> <span class="token function">constructor</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /> <span class="token comment">/**<br /><span class="highlight-line"> * Runs each time the element is appended to the DOM</span><br /> */</span><br /><span class="highlight-line"> <span class="token function">connectedCallback</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>firstElementChild<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Clicked </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> Times</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>Now, whenever the button is clicked, the <code>count</code> and UI are automatically updated.</p>
<p>If you have multiple <code><counter-button></code> elements on a page, each one is self-contained and encapsulated. Updating the <code>count</code> on one doesn't affect the UI of the other.</p>
<p><a href="https://codepen.io/cferdinandi/pen/GRzgpEK">Here's a demo.</a></p>
<h2 id="detecting-attribute-changes">Detecting attribute changes</h2>
<p>The web component lifecycle includes an additional function, <code>attributeChangedCallback()</code>, that runs when attributes on a custom element are added, removed, or changed in value.</p>
<p>You can use it to detect attribute changes and run code in response.</p>
<p>For example, imagine if you wanted to let third-party JavaScript update the <code>count</code> on a <code><counter-button></code> by setting a <code>count</code> attribute, like this...</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">let</span> counter <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'counter-button'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">counter<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'count'</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>First, we need to create a static <em>getter</em> method named <code>observedAttributes()</code>.</p>
<p>This function needs to return an array of attributes to watch. For performance reasons, only attributes listed in this array will be observed by the <code>attributeChangedCallback()</code> function.</p>
<p>We'll return an array with the <code>count</code> attribute.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/**<br /><span class="highlight-line"> * Create a list of attributes to observe</span><br /> */</span><br /><span class="highlight-line"><span class="token keyword">static</span> <span class="token keyword">get</span> <span class="token function">observedAttributes</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token string">'count'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Next, we'll add an <code>attributeChangedCallback()</code> function.</p>
<p>It accepts three arguments: the <code>name</code> of the attribute that's been changed, its <code>oldValue</code>, and its <code>newValue</code>.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">/**<br /><span class="highlight-line"> * Runs when the value of an attribute is changed on the component</span><br /><span class="highlight-line"> * @param {String} name The attribute name</span><br /><span class="highlight-line"> * @param {String} oldValue The old attribute value</span><br /><span class="highlight-line"> * @param {String} newValue The new attribute value</span><br /> */</span><br /><span class="highlight-line"><span class="token function">attributeChangedCallback</span> <span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> oldValue<span class="token punctuation">,</span> newValue</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'changed'</span><span class="token punctuation">,</span> name<span class="token punctuation">,</span> oldValue<span class="token punctuation">,</span> newValue<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>At this point, if we set or update the <code>count</code> attribute on our <code>counter-button</code> element, the <code>attributeChangedCallback()</code> will log some stuff into the console.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token keyword">let</span> counter <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'counter-button'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">// Nothing will happen here, because we're not watching this attribute</span></span><br /><span class="highlight-line">counter<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'hello'</span><span class="token punctuation">,</span> <span class="token string">'you'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">// logs "changed" "count" null 42</span></span><br /><span class="highlight-line">counter<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'count'</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>If we were observing more than one attribute, we would want to first check what the <code>name</code> was before doing anything in the <code>attributeChangedCallback()</code> function.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">/**<br /><span class="highlight-line"> * Runs when the value of an attribute is changed on the component</span><br /><span class="highlight-line"> * @param {String} name The attribute name</span><br /><span class="highlight-line"> * @param {String} oldValue The old attribute value</span><br /><span class="highlight-line"> * @param {String} newValue The new attribute value</span><br /> */</span><br /><span class="highlight-line"><span class="token function">attributeChangedCallback</span> <span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> oldValue<span class="token punctuation">,</span> newValue</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// If the [count] attribute</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>name <span class="token operator">===</span> <span class="token string">'count'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>For this web component, though, the function only runs for the <code>count</code> attribute, so we don't have to check the <code>name</code>, nor do we have to worry about the <code>oldValue</code>.</p>
<p>First, we'll pass the <code>newValue</code> into the <code>parseFloat()</code> method to make sure its to a number. We'll also check if it's <em>Not a Number</em> (<code>isNaN()</code>).</p>
<p>Then, we'll update the <code>count</code> property, and render the new <code>count</code> into the <code>firstElementChild</code> (the <code><button></code>).</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/**<br /><span class="highlight-line"> * Runs when the value of an attribute is changed on the component</span><br /><span class="highlight-line"> * @param {String} name The attribute name</span><br /><span class="highlight-line"> * @param {String} oldValue The old attribute value</span><br /><span class="highlight-line"> * @param {String} newValue The new attribute value</span><br /> */</span><br /><span class="highlight-line"><span class="token function">attributeChangedCallback</span> <span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> oldValue<span class="token punctuation">,</span> newValue</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Make sure the new value is a number</span></span><br /><span class="highlight-line"> <span class="token keyword">let</span> num <span class="token operator">=</span> <span class="token function">parseFloat</span><span class="token punctuation">(</span>newValue<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isNaN</span><span class="token punctuation">(</span>num<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Update the count and render the UI</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>count <span class="token operator">=</span> num<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>firstElementChild<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Clicked </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> Times</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Now, whenever the <code>count</code> attribute is set or updated on our custom element, the <code>count</code> and UI are updated to match.</p>
<p><a href="https://codepen.io/cferdinandi/pen/vYbENzB?editors=1111">Here's another demo.</a></p>
<h2 id="the-shadow-dom">The Shadow DOM</h2>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM">The <em>Shadow DOM</em> is a special DOM</a>, whose contents are separate and often isolated from the main DOM.</p>
<p>Web Components can use the Shadow DOM to <em>encapsulate</em> their child elements, and avoid the naming collisions and unintended side effects that sometimes happen when code is used by teams or across projects.</p>
<p>For example, with our current Web Component, someone could (<em>hopefully accidentally!</em>) wipe out all of the content inside the element.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">let</span> counter <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'counter-button'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">counter<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span></span></code></pre>
<p>With the Shadow DOM, content inside the custom element cannot be accessed or modified with outside JavaScript or CSS.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>counter-button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> #shadow-root (closed)</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Clicked 0 Times<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>counter-button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Inside our constructor, immediately after running the <code>super()</code> method, we can use the <code>Element.attachShadow()</code> method to create a shadow DOM for our web component.</p>
<p>It accepts an object of <code>options</code>. The only one we need to specify is the <code>mode</code>.</p>
<p>If we use <code>open</code> as the value, JavaScript from outside of our web component can access the DOM nodes. With a value of <code>closed</code>, only JavaScript inside the web component itself can access the Shadow DOM.</p>
<p>The <code>Element.attachShadow()</code> method returns to the <code>ShadowRoot</code> object. To easily access it in our methods, we'll assign it to the <code>root</code> property for our instance.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/**<br /><span class="highlight-line"> * The class constructor object</span><br /> */</span><br /><span class="highlight-line"><span class="token function">constructor</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Always call super first in constructor</span></span><br /><span class="highlight-line"> <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Creates a shadow root</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>root <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">attachShadow</span><span class="token punctuation">(</span><span class="token punctuation">{</span>mode<span class="token operator">:</span> <span class="token string">'closed'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// ...</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Now, instead of injecting content into the web component itself (<code>this</code>), we'll use the <code>ShadowRoot</code> (<code>this.root</code>).</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/**<br /><span class="highlight-line"> * The class constructor object</span><br /> */</span><br /><span class="highlight-line"><span class="token function">constructor</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Always call super first in constructor</span></span><br /><span class="highlight-line"> <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Creates a shadow root</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>root <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">attachShadow</span><span class="token punctuation">(</span><span class="token punctuation">{</span>mode<span class="token operator">:</span> <span class="token string">'closed'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Track the count</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// Render HTML</span></span><br /><span class="highlight-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>root<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span></span><br /><span class="highlight-line"> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><button>Clicked </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> Times</button></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p><a href="https://codepen.io/cferdinandi/pen/wvNBKLJ">Here's a demo of our Web Component with the Shadow DOM.</a></p>
<h2 id="opinion-do-not-use-the-shadow-dom-by-default">Opinion: do <em>not</em> use the Shadow DOM by default</h2>
<p>While the Shadow DOM protects your code from unintentional side-effects and interactions, but also introduces some of its own challenges.</p>
<p>One of the biggest ones is around styling. Shadow DOM elements do not inherit global styles like elements in the main DOM do. There are a few techniques for styling elements in the Shadow DOM, but they're all a bit clunky and require more work from you as a developer.</p>
<p>There are also some accessibility challenges related to the Shadow DOM. <a href="https://www.matuzo.at/blog/2023/web-components-accessibility-faq/">Manuel has documented many of them here</a>.</p>
<p>Unfortunately, some of Web Components best features (like <code>template</code> and <code>slot</code>) require the Shadow DOM.</p>
<p>But in my opinion, the tradeoffs and challenges with using the Shadow DOM outweigh the benefits it provides in most cases. It has its place, but the <em>Light DOM</em> (ie. the "regular" DOM) should be the default approach in most instances.</p>
<h2 id="why-use-web-components?">Why use web components?</h2>
<p>You don't <em>need</em> web components to create a counter button... or any component, for that matter. So why use them?</p>
<p>First, they make it a lot more obvious what complex components are in your UI. An element called <code>loading-icon</code> is a lot more obvious than a set of empty nested <code>div</code> elements with a <code>.loading-ring</code> class on it.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- This is clear --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>loading-icon</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>loading-icon</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- This is clunky --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>visually-hidden<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>status<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Waiting to load...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>loading-ring<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>If you're working with a team of developers or creating a design system, web components provide a handful of additional benefits.</p>
<p>First, because the web component generates all of the behind-the-scenes HTML, you don't need to worry about developers forgetting something important.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- This is the wrong class --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>loading-rings<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- This one is missing a div --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>loading-ring<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>If you fix a bug or update your component, you don't have to manually update a bunch of HTML everywhere the component is used. You only need to update the JavaScript.</p>
<p>For example, imagine that the original version of your loading icon component did <em>not</em> include an ARIA live region to announce when the content was loaded to screen readers. You realize the mistake and update the component.</p>
<p>With a traditional HTML component, any developer who uses it will need to update their HTML, JavaScript, and CSS files to include the fix. With web components, they only have to update the JavaScript file.</p>
<pre class="language-html"><code class="language-html"><span class="token comment"><!-- <br /><span class="highlight-line"> No changes needed</span><br /><span class="highlight-line"> The component handles the HTML for you</span><br />--></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>loading-icon</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>loading-icon</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- This is the only change needed --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>loading-icon.v2.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></span></code></pre>
<p>To summarize, the benefits of web components include...</p>
<ul>
<li>Clarity and readability</li>
<li>A simpler and less error prone developer experience</li>
<li>Ease of updating components</li>
<li>Scoping and encapsulation</li>
</ul>
<h2 id="learn-more!">Learn more!</h2>
<p>This article only scratched the surface on Web Components and what they're capable of.</p>
<p>You can <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components">learn more over at MDN</a>. I also have <a href="https://leanwebclub.com/">a standalone course and a project-based workshop on Web Components as part of my Lean Web Club</a>.</p>
The Hellish History of HTML: An incomplete and personal account
2023-12-05T00:00:00Z
https://htmhell.dev/adventcalendar/2023/5/
by Jason Cranford Teague<br><figure class="u-mb">
<a href="https://htmhell.dev/adventcalendar/2023/5/images/HellishHistory.png">
<img src="https://htmhell.dev/adventcalendar/2023/5/images/HellishHistory.png" alt="This timeline shows the adaptation of each version of HTML, along with other key technology releases, phases in the Web design & development, Web eras, and the dominant browser at the time." width="740" height="438" />
</a>
<figcaption>Timeline of HTML from 1990ā2024</figcaption>
</figure>
<p class="highlight">
<strong>Note:</strong> HTML standards are developed first in browsers, so the version might have already became the de facto standard before the official standard document is released.
</p>
<p>The story so far:</p>
<p>In the beginning Tim Berners-Lee created the World Wide Web. This has made a lot of people <a href="https://lareviewofbooks.org/article/the-internet-is-a-crime-against-humanity/" title="The Internet Is a Crime Against Humanity">very angry</a> and ā putting all of humanity in constant contact with each other ā <a href="https://veniceoarsman.com/11932/opinion/essay-how-the-internet-destroyed-the-human-experience/" title="How The Internet Destroyed The Human Experience">been widely</a> <a href="https://www.theatlantic.com/technology/archive/2021/06/the-internet-is-a-collective-hallucination/619320/" title="The Internet Is Rotting">regarded as</a> <a href="https://medium.com/@jamesbridle/something-is-wrong-on-the-internet-c39c471271d2" title="Something is wrong on the internet">a bad</a> <a href="https://www.vanityfair.com/news/2018/07/the-man-who-created-the-world-wide-web-has-some-regrets" title="āI Was Devastatedā: Tim Berners-Lee, the Man Who Created the World Wide Web, Has Some Regrets">move</a>.</p>
<p>This would be called the <em>Hypertext Transfer Protocol</em>, but you probably see it every day as <code>http://</code>. Although initially intended as a way to share scientific papers, Berners-Lee quickly realized it would do a lot more than that:</p>
<blockquote>
I designed it for a social effect ā to help people work together ā and not as a technical toy.
</blockquote>
<p>His vision has led us to today, where the Web is now the predominant information platform for the planet Earth, and the language he created to create documents, HTML, is used by billions.</p>
<p>Unlike other systems at the time that were making use of the growth of home computers and modems ā like <a href="https://en.wikipedia.org/wiki/AOL">AOL</a> and <a href="https://en.wikipedia.org/wiki/CompuServe">Compuserve</a> ā Berners-Lee provided the World Wide Web as a free and open standard:</p>
<blockquote>
The web is for everyone. It should be accessible to people with disabilities and be available in whatever language they speak.
</blockquote>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2023/5/images/Tim_tshirt.png" alt="Tim Burners-Lee and Vinton Cerf stand next to each other wearing black t-shirts with white lettering. Timās shirt says āI didnāt invent the internetā and Vintonās shirt reads āI did not event the Webā" loading="lazy" width="530" height="480" />
<figcaption>
<p>Tim Berners-Lee (left) did <strong>not</strong> invent the Internet. He did invent the Web and HTML. Vinton G Cerf (right) did <strong>not</strong> invent the Web. He and Robert Kahn did invent the Internet.</p>
</figcaption>
</figure>
<h2 id="html-the-early-years-(1990---1991)">HTML: The Early Years (1990 ā 1991)</h2>
<p>In 1990, the Web was at best an āalphaā version of what it would become. Until then, computer networks were generally limited to a local area (LAN) and required a good deal of computer knowledge to use, much less create. There were other contenders at the time that allowed wider area networks (WAN) that could communicate with computers around a country or around the World. Most notable of these was the Internet. My favorite (and arguably the most popular) way of traveling the Internet in the early 1990s was <a href="https://en.wikipedia.org/wiki/Gopher_(protocol)">Gopher</a>. Gopher was (and technically still is) a way to retrieve documents from the Internet stored on a server. However, it quickly fell out of favor after HTTP arrived on the scene. This is, probably for the best. Can you imagine if we were all surfing the Gopher today?</p>
<p>What HTTP had going for it was HTML. Every single web page ever created uses the <em>Hypertext Markup Language</em>. Tim based HTML off of <em><a href="https://en.wikipedia.org/wiki/Standard_Generalized_Markup_Language">Standard Generalized Markup Language (SGML)</a></em> which was already in common use at CERN, mostly by just adding the <code><a></code> tag for hypertext links.</p>
<p>In the early 1990s, HTML was pretty limited. In fact it didnāt include <code><img></code> tags, and its design capabilities were sub-par even when compared to even the clunky word processors of the day. So web pages of the time at best looked like poorly formatted text files.</p>
<p>What the Web did offer, though, was the seemingly magical ability to click a link on one web page and instantly (ok, quickly) load another web page created by anyone else located anywhere in the world.</p>
<p>In 1990, this was big stuff!</p>
<h2 id="html"1"-and-html+-the-wild-frontier-(1991---1995)">HTMLā1ā & HTML+: The Wild Frontier (1991 ā 1995)</h2>
<p>The first public mention of HTML was an informal CERN document in 1991, listing just 18 tags. There are now around 140. The Web in those early years was just an academic curiosity used by Universities⦠well, used by tech geeks at a few universities. The HTML tags were so informal that they were really defined more by whatever the person who wrote the Web browser code wanted them to be and they could (and did) just make up their own.</p>
<p>Although there were a few web browsers available, there was one that rose to popularity amongst the tech geeks of the time. <em><a href="https://en.wikipedia.org/wiki/Mosaic_(web_browser)">Mosaic</a></em>, released in 1993, was co-created by a (at the time) idealistic and starry eyed student by the name of <a href="https://en.wikipedia.org/wiki/Marc_Andreessen">Marc Andreessen</a> atttending the University of Illinois at Urburna-Champaign. Marc decided it was such a good idea that, in 1994, he released <em><a href="https://en.wikipedia.org/wiki/Netscape_Navigator">Netscape Navigator</a></em> as a consumer product. Free from academic constrains, Netscape could add features and functionality to appeal to non-academics as well as add whatever tags they felt appropriate. These tags were referred to as HTML+.</p>
<p>This freedom led to a lot of growth in what the Web could do with tags for greater styling (remember, CSS is still several years off) and structure. Added during this time were tags we take for granted like tables, forms, and inline images.</p>
<p>This freedom also led to a few really terrible tags, for example Netscape introduced <code><blink></code>, which, in case you canāt tell, caused the enclosed content to blink on and off. No, you couldnāt control the speed. No, you couldnāt make it stop. The text just blinked on and off⦠on and off⦠on and off⦠and would keep doing that until you left the page.</p>
<figure class="u-mb">
<picture>
<source srcset="https://htmhell.dev/adventcalendar/2023/5/images/blink_main.gif" media="(prefers-reduced-motion: no-preference)" />
<img src="https://htmhell.dev/adventcalendar/2023/5/images/blink_main.jpg" alt="The blink tag blinks on and off, on and off, on and off⦠to infinity and beyond. Probably best if you canāt see it. Itsā really annoying." loading="lazy" />
</picture>
<figcaption>WHATEVER YOU DO, DONāT BLINK!</figcaption>
</figure>
<p>Unfortunately, this also meant that if you used tags recognized by one browser there was no guarantee they would be implemented or even included by another. This lead to problems when your web page didnāt display the way you intended. Fortunately, HTML is a very forgiving language, and if the browser didnāt recognize a tag it was supposed to just ignore it completely but should still display the content as if there was no tag. Still, imagine if you had set up a data table for your web page. If the userās browser didnāt recognize the <code><table></code> tag, all that information becomes an unintelligible mess.</p>
<p>Another problem on the horizon was that there was no guarantee that the different browser makers would agree on what each tag specifically did or even what to call them. One browser might display a <code><p></code> with the first line indented, while another could require you to add an extra <code><p></code> or <code><br><br></code> to add a space between paragraphs. Interestingly, what we did at the time out of necessity ā no indent and an extra space between paragraphs ā has became the editorial norm we still use today.</p>
<p>There was obviously a need for some kind of standard for HTML if the Web was going to grow out of its infancy.</p>
<p>In 1994, Tim started The World Wide Web Consortium, at first to create standards for HTML that could be agreed upon and used by anyone creating browsers and websites.</p>
<p>Netscape would rule the early web until Microsoft, seeing the threat to their desktop market, ignited the bitter struggle known to legend as <em>The Browser Wars</em>.</p>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2023/5/images/NSvIE.jpg" alt="Netscape VS Microsoft Internet explorer buttons next to each other. The Netscape button reads āNetscape Now! 3.0ā with the Netscape Logo. The IE button reads āFree Microsoft Internet Explorerā With the IE logo." loading="lazy" />
<figcaption>These buttons were ubiquitous in the 1990s World Wide Web, often accompanied a message reading āBuilt for Netscapeā or āBuilt for Internet Explorerā</figcaption>
</figure>
<div class="highlight u-mb">
<h3 id="my-own-private-htmhell-slammin-ps">My Own Private HTMHell: Slamminā Ps</h3>
<p>I was first introduced to the Web and HTML in the fall of 1994, when I started at Rensselaer Polytechnic Institute (RPI) for my Masters in Communication. Iād heard some rumbling about this new fangled Web thingy earlier in the year while working computer tech at another University, but I had my Gopher and that was good enough for me.</p>
<p>At RPI, I fell in with a rowdy crowd of Communication PhD and Master students, one of whom had co-authored the first book about the web, <em><a href="https://www.amazon.com/exec/obidos/ASIN/0672306174">The World Wide Web Unleashed</a></em>.</p>
<p><a href="https://www.linkedin.com/in/johndecember/">John December</a>, for that was his name, had also started what was, arguably, the first online only magazine, called <em><a href="https://johndecember.com/cmc/mag/archive/">Computer Mediated Communications</a></em>, a few months before I got there. There was no real design to it beyond the basic HTML, so he brought me in to be the first designer.</p>
<p>At the time, being able to display images was still pretty new, and background images could only be applied to the web pages entire background and had to tile vertically and horizontally without stopping. Good enough to add a bit of color or texture, but not much else.</p>
<p>To create web pages required you to code them using a command line prompt in a terminal. I still get hives at the very mention of using a command line, but I was determined to learn HTML. So, despite my trepidation, I tucked into command line editing.</p>
<p>To code and write the pages you had to go back and forth between code mode and text mode. In code mode you could add tags but not text. In text mode you could add text but any code would be treated as text. Switching between these modes could take up to 30 seconds to load each time.</p>
<p>If you forgot to add the <code><p></code> in code mode or thoughtlessly added them in text mode (which happened painfully often), you had to go back, delete all of the <code><p></code>s in text mode and reinsert them in code mode which could take a lot of time as the system lumbered between modes ā you added the tag and then switched back to go to the next spot to add the next tag. I spent many hours doing that at first.</p>
<p>To speed production, we would set up our basic web page code structure and then simply add a bunch of <code><p></code> tags (there wasnāt a close paragraph tag at the time) and start writing an article, skipping over the next <code><p></code> to start a new paragraph.</p>
<p>We referred to this process as āSlamminā Pāsā.</p>
</div>
<h2 id="html2-the-standards-rise-(1995---1997)">HTML2: The Standards Rise (1995 ā 1997)</h2>
<p>Realizing that all of these different browsers needed to agree on exactly what tags were available in HTML and at least a rough guideline on how they should present the content, Tim started <a href="https://www.w3.org/" title="The World Wide Web Consortium">The World Wide Web Consortium</a>.</p>
<p>The W3C would become the recognized body for setting Web technology standards, including (amongst many others) HTML, CSS, SVG, XML, and Web Accessibility. The one exception to this is JavaScript, which is controlled by <a href="https://www.ecma-international.org/">ECMA International</a>.</p>
<p>HTML2 attempted to make order out of the chaos that HTML had quickly become. Although not introducing any particularly exciting features, it codified the existing features to give everyone a level playing field to work on.</p>
<p>I just want to reiterate that at this point in the Web, CSS (which today does the lionās share of actual visual design on the Web) and JavaScript (which does the lionās share of interactivity on the web) were both new technologies that even if implemented had a lot of cross-browser inconsistencies. JavaScript caught on fast, but it would be the turn of the century before CSS would become the default way to add a visual layer to HTML code.</p>
<div class="highlight u-mb">
<h3 id="my-own-private-htmhell-color-me-unimpressed">My Own Private HTMHell: Color Me unimpressed</h3>
<p>In the 1990s most color monitors were limited to just 216 colors. Let me make this clear, since we live in a world of monitors that can display millions of colors: A single pixel on the overwhelming majority of computer monitors in the 1990s could only display one of 216 colors at a time. This limited the color palette for web designs to what became referred to as the <em>Web Safe Colors</em>, which, today, we call just <a href="https://en.wikipedia.org/wiki/Web_colors">Web Colors</a>. Although other colors could be simulated by placing two different colors together and letting the viewers eye blend them (like a <a href="https://en.wikipedia.org/wiki/Pointillism">pointillism</a> painting), the effect was never satisfying, and drop shadows and gradients were never smooth. <a href="https://archive.org/details/coloringwebgraph0000wein/mode/2up">Entire books</a> were devoted to using Browser Safe Colors most effectively. I even kept a poster on my office</p>
<p>When I would try to sneak a drop shadow into a design (I had a high-end monitor that could display thousands of colors!), my creative director would (metaphorically) slap me on the wrist and tell me to cut it out.</p>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2023/5/images/WebmasterPalette.jpg" alt="Poster of the 216-Color Webmasterās Palette with RGB values" loading="lazy" />
<figcaption>In my day we only had 216 colors to design with and we liked it!</figcaption>
</figure>
</div>
<h2 id="html32-short-and-sweet-(1997)">HTML3.2: Short and Sweet (1997)</h2>
<p>Although it didnāt last long as a standard, HTML3, brought the standard fully under the control of the W3C. Despite working on it for almost two years, HTML 3.0 was never released as a standard. According to the W3C, the difference between HTML 2.0 and HTML 3.0 was, āso large that standardization and deployment of the whole proposal proved unwieldyā and was eventually dropped.</p>
<p>Instead of starting from scratch, the W3C worked to refine the 2.0 standard, releasing HTML 3.2 (code named <em>wilbur</em> for reasons no one is sure of) in January of 1997 HTML4 was released a little less than twelve months later in December.</p>
<p>By 1997, the Web was beginning to take off as a commercial platform. HTML 3.2 standardized features we take for granted today like tables, text flow around images, and inputs. Crucially, HTML 3.2 was backwards compatible with HTML 2, meaning that older Web pages could be displayed, something that still holds true to today.</p>
<p>Although some tags were <a href="https://en.wikipedia.org/wiki/Deprecation?wprov=sfti1#">depreciated</a> ā most notably <code><blink></code> and <code><marque></code> to the celebration of many ā it secured HTMLās future as a stable standard that could be relied on. In fact, the one serious attempt to move away from the principle of backwards compatibility ended in time waisting disgrace. But weāll talk more about XHTML2 in a bit.</p>
<p>Although short lived, the <a href="https://www.w3.org/press-releases/1997/html32-rec/">HTML 3.2 recommendation</a> set the stage for keeping the standard free and open, so that no one company could monopolize or privatize it. Instead, the W3C the took public input and invited experts and company representatives to help guide its development and approve the final recommendations, another principal the W3C still follows.</p>
<p>Sometime around 1996ā97, the Web began to quickly gain in popularity as more and more people saw its usefulness. There were publications moving online making news instant, online stores that could undersell their brick-and-mortar competitors (thanks Amazon), and much improved web search capabilities (thanks Google). Oh, and there were apparently some photographs of scantily clad men and women⦠ummm⦠exercising together.</p>
<p>This was a time known as the Dot-com Boom.</p>
<div class="highlight u-mb">
<h3 id="my-own-private-htmhell-on-the-table">My Own Private HTMHell: On the Table</h3>
<p>Before CSS introduced the float element, and long before the flex and grid standards, we used tables to create web page layouts. The <code><table></code> tag was not conceived for this purpose (it was for presenting tabular information and data) but web designers quickly realized they could repurpose it to create columns and rows for <a href="https://en.wikipedia.org/wiki/Grid_(graphic_design)">design grids.</a> This allowed us a lot more flexibility and spurred a lot of design innovation.</p>
<p>But tables were far from perfect. Although you could turn off the table cell borders and collapse gaps between them, many people didnāt. So Web design from that time is often typified by clunky ridged gray borders and pixelated background images.</p>
</div>
<h2 id="html4-boom-goes-the-web-(1997---2014)">HTML4: Boom Goes the Web (1997 ā 2014)</h2>
<p>It was the best of times, it was the worst of times. The HTML4 standard was released and JavaScript and CSS, despite browser inconsistencies, were being adapted to create better designs for a more dynamic Web.</p>
<p>HTML4 improved tables and forms, introduced support for CSS and JavaScript, allowed better accessibility features and internationalization, you could embed external content with the <code><iframe></code> tag (the much maligned <code><frame></code> tag was deprecated), and plugins like Flash could be added using the <code><embed></code> tag.</p>
<p>A gold rush began, with start-up companies springing up like weeds. This period was know as the Dot-Com Boom. Everybody and their brother had an idea for how to make money out of the Web, and they all needed someone who knew HTML. Jobs quickly flourished for Information Architects, Visual Designers, and Programmers. The good times of free gourmet meals at work, video arcade break rooms, and corporate parties on cruise lines seemed like it would never end.</p>
<p>At the same time, two different Web browsers were vying for the hearts and minds of the Web viewing public. This was a time known to Web lore as ā<a href="https://en.wikipedia.org/wiki/Browser_wars" title="The Browser Wars">The Browser Wars</a>ā as Microsoft Internet Explorer and Netscape Navigator battled it out. Maybe we should really call this The First Browser War or The Great Browser War or Browser War I or maybe even WWWWAR I. As we shall see, another browser war was less than ten years away and another one may be going on now.</p>
<p>The Dot-com boom turned out to be a bubble and the bubble began to burst in late 2000. I will not dwell too long on this sad chapter in Web history, only to say that it had a profound effect on the burgeoning Web professionals of the day. Many were unceremoniously dumped from their first job ā their dream job ā crying over stock options that went from millions to pennies on the dollar all while watching the entire industry seemed to be imploding around them.</p>
<p>That said, the Dot-com bust cleared the way for a rethinking of how the web would work. Just a few years later, there was an explosion of new sites that were finally realizing the promise of the Web. Although Web 2.0 is often thought of as starting in the early 2000s, the power that drove it had been around since the mid-1990s when Cascading Style Sheets and Javascript (the duo technologies often referred to together at the time as DHTML or Dynamic HTML) became standards adopted by Web browsers.</p>
<p>What had held the development back was the technical debt of how the web was started and inconsistencies in how browsers displayed the Core Web Standards (HTML, CSS, JavaScript). What would look great in Netscape might fall apart in Internet Explorer, so we developed a lot of work arounds and <a href="https://en.wikipedia.org/wiki/Kludge">kludges</a> or just limited designs for cross-browser compatibility.</p>
<p>For better or worse, by 2001 Microsoft came out on top, and Web designers could begin to rely on a consistent, if lack luster in its standards adherence, Web browser to create for. Web2.0 could now actually take off.</p>
<p>After that, things simmered down for a bit in a period I refer to as āPax Webā, and Flash, although not an open standard like HTML, was quickly adapted to bring more video and interactive content to the Web. Flash was easy enough for designers to use without having to learn a lot of code, and allowed for dynamic and highly interactive designs, something we struggle to recreate on the Web even today <a href="https://www.howtogeek.com/805605/this-is-how-steve-jobs-killed-adobe-flash/">after Flashās demise at the hands of the Apple iPhone</a>.</p>
<div class="highlight u-mb">
<h3 id="my-own-htmhell-internet-explorer-6">My Own HTMHell: Internet Explorer 6</h3>
<p>Ask any Web designer or developer working in the 2000s what it was that woke them up screaming in the middle of the night in panic and rage, the answer will likely be Internet Explorer 6. Launched in 2001 and preinstalled on every Windows computer as the default browser, it became <em>the Internet</em> to many people.</p>
<p>While talking to a class of fourth graders in the mid-2000s about the Web, I wanted to use their class computer to show some examples and asked the teacher if they had a Web browser. Her response was, āNo, we just have the Internet,ā and pointed to the Internet Explorer Icon.</p>
<p>Despite being the window to the web for billions (at its height, IE6 had something like around 80% of the world browser market), it had spotty, incomplete, inconsistent and buggy Web standards implementations. This made the job of creating Web sites difficult at the best of times, especially if you wanted to stick to standards and maintain cross-browser support.</p>
<p>I spent many, many, MANY hours during that time adjusting and re-adjusting my HTML, CSS, and JavaScript, using arcane kludges ā like playing a sadistic whack-a-mole where whacking one guarantees another five popping up ā and often just having to settle for the lowest common denominator to get what I wanted to display the way I wanted it to display the same way on every browser.</p>
</div>
<h2 id="xhtml1-strict-machine-(2000---2008)">XHTML1: Strict Machine (2000 ā 2008)</h2>
<p>Although HTML4 solidified the Web, there were those who thought it too loose-goose to be considered a real solution for data mark up. HTML was very forgiving. It didnāt <em>require</em> that you follow proper syntax and anything the browser didnāt understand it would just throw out. That is browsers <em>except</em> IE6, of course, which would just choke (more HTMHell).</p>
<p>In 2000, the W3C released <a href="https://www.w3.org/TR/xhtml1/" title="XHTML⢠1.0 The Extensible HyperText Markup Language (Second Edition)">XHTML 1.0</a> as a reformulation of HTML using XML syntax. Extensible Markup Language (XML) had been around for a while as a markup language used to structure and organize data. The W3C decided to establish a competing standard to provide easier integration with XML code. XHTML aimed to combine the best of HTML and XML, providing stricter rules for markup and enabling interoperability with XML-based technologies.</p>
<p>XHTML was stricter than simple HTML, meaning that syntax couldnāt be fudged, but creating a page using XHTML was compatible with and little different than creating one in HTML4. However, it did force developers to be more semantic, consistent, and accessible with their code, something still important today.</p>
<h2 id="xhtml2-look-back-in-anger-(doa)">XHTML2: Look Back in Anger (DOA)</h2>
<p>The idea behind XHTML2 was bold, brave, and completely ineffective. Rather than simply evolve HTML and XHTML, <a href="https://www.w3.org/TR/2010/NOTE-xhtml2-20101216/" title="XHTML⢠2.0">XHTML2</a> sought to start over and create a stricter and more modular version the Web markup language. It was heavily based on XML and eliminated all presentation elements in favor of CSS. The idea was to force the complete separation of structure from presentation thus allowing for the code to be device independent.</p>
<p>There was just one problem: XHTML2 would not be backwards compatible.</p>
<p>HTML had, since its beginning, been backwards compatible, meaning that older Web pages could display even in browsers using newer HTML standards. XHTML2 would have quite literally broken the Web. In addition, the new language was far more complex, and stricter enforcement of syntax and structure made creating Web pages much more difficult without a programming background.</p>
<p>Development of<a href="https://hsivonen.fi/xhtml2-html5-q-and-a/" title="An Unofficial Q&A about the Discontinuation of the XHTML2 WG"> XHTML2 was eventually abandoned in 2009</a> as interest in a more promising standard was catching on.</p>
<h2 id="html5-a-new-hope-(2014---now)">HTML5: A New Hope (2014 ā Now)</h2>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2023/5/images/front-end-stack.png" alt=" Logos for HTML5 (red), JS (yellow), and CSS3 (blue)" loading="lazy" />
<figcaption>Core Web Technologies: HTML 5, JavaScript, and CSS 3</figcaption>
</figure>
<p>Although not officially released as a standard until 2014, HTML5 had already become the de facto standard in Web browsers starting around 2010. Coupled with a developing CSS3 and a mature JavaScript, the Web was poised for a new phase. The rise of mobile devices being used to surf the Web meant that we had to start thinking more and more about how Web pages would look on small screens. Rather than creating a site for desktop/laptop and one for mobile, CSS could be used to change the layout of the HTML in response to the screen size. This became most commonly known as <em><a href="https://alistapart.com/article/responsive-web-design/" title="Responsive Web Design
by Ethan Marcotte">Responsive Web Design</a></em>.</p>
<p>HTML5 made an accessible and semantic Web its priority. Semantic HTML code simply means that it is well structured and that every item on the page has a clearly defined purpose. So <a href="https://dev.opera.com/articles/new-structural-elements-in-html5/" title="New Structural Elements in HTML5">structural HTML tags</a> such as <code><nav></code>, <code><section></code>, <code>article</code>, and <code><figure></code> were added. New input types were added as well, so that the data could be more easily differentiated (if only developers would use them properly!).</p>
<p>While XHTML2 was discontinued, the idea of combining HTML and XML syntax continued to be explored leading to XHTML5. Like XHTML1, XHTML5 allows developers to write web pages using the stricter rules of XML while still benefiting from the features and compatibility of HTML5.</p>
<p>Because Internet Explorer was languishing without updating to modern standards (IE 7 improvements came too little too late in 2006 and took years to replace IE6), Chrome was at the forefront of implementing HTML5 and CSS3. The upstart browser quickly caught on and rapidly grew in popularity.</p>
<p>The beginning of the end of WWWWar II came in <a href="https://gs.statcounter.com/browser-market-share/desktop/worldwide/#monthly-200901-201612" title="Desktop Browser Market Share Worldwide
Jan 2009 - Dec 2016">2012 when Chrome unseated Internet Explorer as the top browser world wide</a> and IE never recovered after that. Although IE was still a major player for several years, by 2015, seeing that Internet Explorer was too tech debt laden to compete, Microsoft launched a completely new Web browser: Edge. Internet Explorer would finally be discontinued in 2022, but there are signs that Microsoft is gearing up for another fight.</p>
<figure class="u-mb">
<img src="https://htmhell.dev/adventcalendar/2023/5/images/StatCounter-browser-ww-monthly-200901-201612.png" alt="Chart from StatCounter showing the desktop browser market share worldwide from January 2009 through December 2016. Google Chrome surpasses Microsoft Internet Explorer market share in mid-2012. " loading="lazy" />
<figcaption>In 2012, Google Chrome passed Microsoft Internet Explorer as the most popular web browser in the world. The WWW would never be the same.</figcaption>
</figure>
<p>Two other huge advances in Web design happened around 2009. First was that CSS Level 3 had become a de facto ā if not finalized ā standard. This revised and expanded styling standard was quickly adapted by browser makers eager to push their browsers as the most standard compliant. Almost as importantly, and long overdue, was the ability to use downloadable font files to use virtually any typeface in Web designs. This push, led the way by Safari was quickly introduced into Chrome and finally a few years later begrudgingly by Internet Explorer.</p>
<p>Additionally, since Apple refused to support Flash on its extremely popular iPhone, Flash quickly fell out of favor as a way of delivering video and interactive content. Fortunately, HTML5 included new (and old) standards for embedding video, and <a href="https://en.wikipedia.org/wiki/SVG">Scalable Vector Graphics (SVG)</a>, an old Web standard many had all but given up on, saw a resurgence in popularity to replace Flashās vector capabilities.</p>
<p>The explosion of typography on the web, the new capabilities in CSS3, and the resuscitation of SVG led to a renaissance in Web design in the 2010s. Designers, having to move away from Flash so that they could support mobile devices, were now also expected to create designs that would work just as well on devices from a few hundred to a few thousand pixels wide. In effect, we were looking at an entirely new Web by the end of the 2000s.</p>
<p>When an evolutionary change like this happened in the early-2000s, we called it Web 2.0. I would argue that the period from roughly 2009 ā 2016 should be referred to as Web 3.0. Despite the terms Web3 and Web 3.0 being in common usage today to describe a decentralization of the Web (the term was first used sometime in the mid to late 2010s, but is still little more than a marketing buzzword) we missed an entire phase where the Web evolved off the desktop and became mobile, leading to a need to radically rethink our approach to Web design as ā<a href="https://bradfrost.com/blog/post/mobile-first-responsive-web-design/">Mobile First</a>ā strategies developed.</p>
<h2 id="the-future-of-html?">The Future of HTML?</h2>
<p>So, where are we now? Are we already living in a Web3 world already? Maybe.</p>
<p>We hear a lot of discussion about the <a href="https://3327.io/user-decentric-design-in-web3/" title="USER (DE)CENTRIC DESIGN IN WEB3">next web phase being āWeb 3ā</a> but many others are <a href="https://web3isgoinggreat.com/" title="Web3 is Going Just Great">less than thrilled by the prospect</a>. There is little doubt that userās control of their data has eroded to the point of non-existence in the last thirty years. At the heart of Web3 (beyond cryptocurrency and blockchain) is a desire to bring back a decentralized Web with greater individual user control. While this is unlikely to affect the HTML standard, it may radically alter the way we use it.</p>
<p>To complicate matters further, we appear to be <a href="https://interestingengineering.com/culture/is-microsoft-launching-a-new-browser-war" title="Is Microsoft Launching a New Browser War?">heading into a new Browser War</a>, with Microsoft recently launching several shots across the bow of Googleāoh, excuse me, I mean āAlphabetāāleveraging Windows OS to push users towards a newly revitalized Edge Browser and away from Chrome.</p>
<p>Despite all of this, HTML5 is not going anywhere soon. Unlike previous versions, HTML5 is modular. That is, rather than rereleasing the entire standard as a single document, new capabilities are added to the existing standard. Older standards are rarely, if ever, changed.</p>
<p>That said, the W3Cs <a href="https://open-ui.org/" title="Open UI">Open UI Group</a> is addressing a long standing problem with HTML: user interface control appearance and behavior.</p>
<blockquote>Most complex web projects today need far more than what HTML5 form and UI controls provide.</blockquote>
<p>Currently, web developers will use a lot of CSS and Javascript to add capabilities that would be better handled by built in HTML components, but there are only limited ways to style these elements and none for how they function without resorting to scripting. Although still in its infancy, Open UIs goal is to web developers more control to style and extend web UI components without changing the underlying HTML code.</p>
<p>In a little more than 30 years, the Web has gone from being a Wild Frontier to becoming an essential utility humanity relies on to communicate, and HTML will always be the core of the Web.</p>
Back to Basics: 5 HTML attributes for improved accessibility and user experience
2023-12-04T00:00:00Z
https://htmhell.dev/adventcalendar/2023/4/
by Daniela Kubesch<br><p>In the fast-paced world of web development, it's easy to get caught up in the latest frameworks, libraries and cutting-edge technologies. But sometimes, the most impactful improvements come from revisiting the fundamentals.</p>
<p>In this blog post, I'll guide you through five HTML attributes that not only improve accessibility but also enhance the overall user experience. Whether you are an experienced developer or just starting, let's explore the potential of these elements to create a more inclusive web experience.</p>
<h2 id="1-hreflang">1. hreflang</h2>
<p>The <a href="https://www.w3.org/TR/2014/REC-html5-20141028/links.html#attr-hyperlink-hreflang"><code>hreflang</code> attribute</a> <strong>specifies the language of a linked resource</strong> on an <code><a></code> or <code><link></code> element. It works like the <code>lang</code> attribute but is specifically used for links.</p>
<h3 id="why-use-hreflang?">Why use <code>hreflang</code>?</h3>
<p>You can improve the user experience and SEO by using <code>hreflang</code> for both internal and external site links.<br />
Using <code>hreflang</code> on internal website links provides a way to tell search engines about different variations of a page for other languages or regions. This means that English speakers would receive the English version of the site, while Swedish speakers would receive the Swedish version. There is no need for manual switching on the user's end, resulting in a smoother experience.</p>
<h3 id="how-to-use-hreflang">How to use <code>hreflang</code></h3>
<p>Add the <code>hreflang</code> attribute with the needed <a href="https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry">ISO language code</a> to your <code><a></code> element. For an English website, this would be <code>en</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>English Website<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>You can also be more specific with the language code and use regional variations. For example, we can add <code>en-GB</code> for British English.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.at<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en-GB<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>English Website<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>If your website is available in multiple languages, you can use <code>hreflang</code> to specify the language and region of a document associated with a specific URL. Adding the attribute will help search engines understand the language and regional targeting of different web page versions.</p>
<p>Add <code>hreflang</code> with the required language code to each link. One link should act as the default fallback version, identifiable by adding <code>hreflang="x-default"</code> instead of the language code. Lastly, set the value of the <code>rel</code> attribute to <code>"alternate"</code> for each link in the language switcher to indicate that the linked pages are alternatives to the present page.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</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>https://example.com<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-default<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</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>https://example.com/de<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>You can also use <code>hreflang</code> in your language switcher.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-default<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>English<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com/de<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>German<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>Sometimes, language switchers use link text in the language they are switching to. You can indicate this by additionally using the <code>lang</code> attribute.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-default<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>English<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com/de<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Deutsch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight">
<strong>Note:</strong> Adding the <code>lang</code> attribute is particularly important for assistive technology users. For instance, screen readers alter their voice and pronunciation based on the language attribute.
</p>
<p>An additional way to enhance accessibility is to include <code>aria-current="true"</code> to the presently active link.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-default<span class="token punctuation">"</span></span> <span class="token attr-name">aria-current</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>English<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com/de<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Deutsch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="2-translate">2. translate</h2>
<p>The <a href="https://html.spec.whatwg.org/multipage/dom.html#attr-translate"><code>translate</code> attribute</a> is used to <strong>indicate whether an element should be translated</strong> or not.</p>
<h3 id="why-use-translate?">Why use <code>translate</code>?</h3>
<p>Most website text is translatable by default (with some exceptions such as text on images or within SVGs). Translation tools, such as Google Translate, may suggest translation of page contents if the site's defined language differs from the browser's default language. However, there may be instances where this behaviour is unwanted.<br />
Specific terms like company names, e-mail addresses or code examples should generally not be translated to avoid confusion. Automated translations are not always completely accurate, particularly with niche or technical words.</p>
<h3 id="how-to-use-translate">How to use <code>translate</code></h3>
<p>You can use translate on any HTML element. Assign an empty string (<code>""</code>) or <code>yes</code> for translation and <code>no</code> to avoid translation.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Original German text --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Wien<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> ist (wieder) die lebenswerteste Stadt der Welt!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">translate</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Wien<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> ist (wieder) die lebenswerteste Stadt der Welt!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- What it would look like translated into English --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Vienna<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> named world's most liveable city (again)!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">translate</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Wien<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> named world's most liveable city (again)!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span></code></pre>
<h2 id="3-reversed">3. reversed</h2>
<p>The <a href="https://html.spec.whatwg.org/multipage/grouping-content.html#attr-ol-reversed"><code>reversed</code> attribute</a> is used to <strong>reverse ordered lists</strong> (<code><ol></code>) in the opposite order.</p>
<h3 id="why-use-reversed?">Why use <code>reversed</code>?</h3>
<p>Using the <code>reversed</code> attribute keeps the order of visual and semantic list items the same, but they are numbered from highest to lowest. This means adding this attribute to your ordered list (<code><ol></code>) will not reverse the list items but only the list numbers. For instance, this behaviour is helpful if you want to count down your top five desserts. However, the reversed attribute does not affect unordered lists (<code><ul></code>).</p>
<p class="highlight">
<strong>Note:</strong> The reversed attribute does not affect unordered lists (<code><ul></code>).
</p>
<h3 id="how-to-use-reversed">How to use <code>reversed</code></h3>
<p>Add the <code>reversed</code> attribute to your list element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ol</span> <span class="token attr-name">reversed</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Cookies<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Crema Catalana<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Tiramisu<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Pastel de Nata<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Sacher cake<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ol</span><span class="token punctuation">></span></span></span></code></pre>
<p>This will result in the following reversed list:</p>
<ol reversed="">
<li>Cookies</li>
<li>Crema Catalana</li>
<li>Tiramisu</li>
<li>Pastel de Nata</li>
<li>Sacher cake</li>
</ol>
<p class="highlight">
<strong>Note:</strong> Screen readers announce the list in DOM order along with the correct number: "5. Cookies, 4. Crema Catalana, etc.".
</p>
<h2 id="4-controls">4. controls</h2>
<p>The <a href="https://html.spec.whatwg.org/multipage/media.html#attr-media-controls"><code>controls</code> attribute</a> instructs the browser to <strong>show the standard video or audio controls</strong>.</p>
<h3 id="why-use-controls?">Why use <code>controls</code>?</h3>
<p>Including playback controls on your video and audio content is essential for optimal user experience, accessibility and usability. These controls enable users to stop or adjust video/sound playback and assist those who may experience motion sickness or distraction while browsing your website.<br />
The default controls include the playback essentials such as play, pause, seek (moving position) and volume, as well as fullscreen toggle, <a href="https://htmhell.dev/tips/the-track-element/">captions/subtitles and track</a> for video content only.</p>
<p class="highlight">
<strong>Note:</strong> The browser controls provided as defaults should be used with caution. Sometimes keyboard-based navigation can cause problems, such as loss of focus, forcing the user to reposition themselves. Consider <a href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility/Multimedia#creating_custom_audio_and_video_controls">implementing custom controls</a> or integrating an external media player equipped with accessibility features. <strong>However, it is still preferable to include controls than not to.</strong>
</p>
<h3 id="how-to-use-controls">How to use <code>controls</code></h3>
<p>You can add the <code>controls</code> attribute to the <code><video></code> or <code><audio></code> element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>video</span> <span class="token attr-name">controls</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</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>movie.mp4<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>video/mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</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>movie.ogg<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>video/ogg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>track</span> <span class="token attr-name">kind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>captions<span class="token punctuation">"</span></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>sampleCaptions.vtt<span class="token punctuation">"</span></span> <span class="token attr-name">srclang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>track</span> <span class="token attr-name">kind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>subtitles<span class="token punctuation">"</span></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>sampleSubtitles_en.vtt<span class="token punctuation">"</span></span> <span class="token attr-name">srclang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>track</span> <span class="token attr-name">kind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>subtitles<span class="token punctuation">"</span></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>sampleSubtitles_de.vtt<span class="token punctuation">"</span></span> <span class="token attr-name">srclang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> Your browser does not support the video tag.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>video</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight">
<strong>Note:</strong> The default controls cannot be styled with CSS.
</p>
<h2 id="5-autocomplete">5. autocomplete</h2>
<p>The <a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete"><code>autocomplete</code> attribute</a> <strong>enables the browser to complete form values automatically</strong> and can be applied to the <code><form></code>, <code><input></code>, <code><select></code>, and <code><textarea></code> elements.</p>
<h3 id="why-use-autocomplete?">Why use <code>autocomplete</code>?</h3>
<p>With the <code>autocomplete</code> attribute present, a browser can suggest a previous user's input for a form field's value, meaning a user does not have to remember or manually enter personal information. This results in significant benefits for individuals with cognitive impairments, reduced mobility, attention deficits, low vision, or blindness. Some people may find reduced manual input in forms particularly beneficial by lowering the need for extensive typing.</p>
<p>The <code>autocomplete</code> attribute improves the usability and efficiency of an HTML form for users by informing browsers and assistive technologies about the intended use of a specific form field. Screen readers use these autocomplete values to understand the nature of input fields and help users enter information more efficiently. For example, if a user's browser has saved information for a particular field (such as their name or e-mail), the screen reader will prompt them to fill in that information automatically.</p>
<h3 id="how-to-use-autocomplete">How to use <code>autocomplete</code></h3>
<p>You can assign different values to <code>autocomplete</code>.</p>
<p>If you choose <code>off</code>, the browser cannot automatically enter or select a value for this field.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>E-Mail<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<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>email<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>email<span class="token punctuation">"</span></span> <span class="token attr-name">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>off<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p class="highlight">
<strong>Note:</strong> Many password managers may still prompt the user to save information or use previously stored data.
</p>
<p>If you choose <code>on</code>, the browser will complete the input automatically. However, since no further information concerning the expected data is given, the browser may use its own judgement.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">action</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 attr-name">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>on<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>firstname<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First Name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>firstname<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>firstname<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lastname<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Last Name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lastname<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>lastname<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Email<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<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>email<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>The optimal solution is to specify distinct values for the required data by opting for the corresponding value from a <a href="https://www.w3.org/TR/WCAG21/#input-purposes">list of available input purposes</a>.</p>
<p>Your form could look like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">action</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 attr-name">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>on<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>firstName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First Name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>given-name<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>firstName<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>firstName<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 punctuation">/></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lastName<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Last Name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>family-name<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lastName<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>lastName<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 punctuation">/></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>email<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Email<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">autocomplete</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<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>email<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>email<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight">
<strong>Note:</strong> The absence or incorrect use of an <code>autocomplete</code> attribute in an input field can cause inconvenience to users, particularly those with cognitive disabilities. Browsers cannot suggest accurate values, making it difficult for individuals to provide the expected input. Please make sure you choose the correct value.
</p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope the exploration of these attributes has sparked your interest. When you embark on your next project, remember the significance of prioritising usability, accessibility and simplicity.</p>
The Form Attribute - Enhancing Form Layout Flexibility
2023-12-03T00:00:00Z
https://htmhell.dev/adventcalendar/2023/3/
by Alexander Muzenhardt<br><p>Consider a scenario where you have a login form containing two input fields with corresponding labels, alongside a submit and a reset button. If you submit the form, the action of the form gets triggered, and you can work with the <code>formData</code>.</p>
<p>The layout looks nice as well (letās imagine we have added some fancy CSS).</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">action</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/login<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>get<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<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>username<span class="token punctuation">"</span></span> <span class="token attr-name">required</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Password<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<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>password<span class="token punctuation">"</span></span> <span class="token attr-name">required</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>reset<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Reset<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="the-challenge">The Challenge</h2>
<p>Now, let's delve into a new requirement presented by your client. They want a revamped layout with the buttons placed elsewhere. This redesign, however, leads to a problem.<br />
The buttons are no longer within the form element which causes the buttons to lose their association to the form.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">action</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/login<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>get<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<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>username<span class="token punctuation">"</span></span> <span class="token attr-name">required</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Password<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<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>password<span class="token punctuation">"</span></span> <span class="token attr-name">required</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /> <span class="token comment"><!-- Because of the new layout, we have to move the buttons <br /> outside of the form --></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>reset<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Reset<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span></code></pre>
<p>If you try to submit or reset the form, nothing will happen.</p>
<p>These buttons must be within the form element to be functionally associated with it.<br />
Unfortunately, the client insists on the new layout and to move the buttons outside the form element is the only option you have.</p>
<h2 id="the-solution">The Solution</h2>
<p>If you read the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button?retiredLocale=de#form">button elementās specification</a>, there is an interesting attribute called <code>form</code>. This is very helpful in your situation and will solve your problem. You can add the <code>form</code> attribute to each button and assign a name to it. The name can be whatever you want. A good name would be <em>form-login</em>.<br />
With that change, you just have to add an <code>id</code> attribute to the form element itself. The value of the attribute has to be the same as in the <code>form</code> attribute. That's it!</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">action</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/login<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>get<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>form-login<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<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>username<span class="token punctuation">"</span></span> <span class="token attr-name">required</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Password<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<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>password<span class="token punctuation">"</span></span> <span class="token attr-name">required</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token comment"><!-- Because of the new layout, we have to move the Buttons outside the form --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>reset<span class="token punctuation">"</span></span> <span class="token attr-name">form</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>form-login<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Reset<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">form</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>form-login<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"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span></code></pre>
<p>With these adjustments, your form works like before but with the freedom of positioning your buttons as you want.</p>
<h2 id="a-word-on-practicality">A Word on Practicality</h2>
<p>The same approach can be applied to the <code>input</code>, <code>textarea</code>, <code>select</code> and other <code>form</code> elements as well. You can check the MDN or the <a href="https://html.spec.whatwg.org/#toc-semantics">HTML spec</a> to see if an element accepts this attribute.<br />
With that approach elements are no longer required to be children of the form element. You are able to place your elements as you wish in the DOM, and they are always associated with the <code>form</code> element.</p>
<h2 id="accessibility-concerns">Accessibility concerns</h2>
<p>With this approach, you will get a lot of freedom with your layout. However, it is crucial to maintain an order in the DOM that still makes sense to users. Otherwise, it can be confusing and frustrating.</p>
<p>For example, if the position of the button in the DOM in the previous example is not close to the rest of the form elements, screen-reader or keyboard user may have difficulty locating the button and understanding its relation to rest of the form.</p>
<h2 id="browser-compatibility">Browser compatibility</h2>
<p>The <code>form</code> attribute is well-supported, with Internet Explorer being an exception.<br />
For detailed browser compatibility information, refer to <a href="https://caniuse.com/form-attribute">caniuse</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>With the <code>form</code> attribute you are no longer restricted to position your inputs and buttons inside the <code>form</code> element boundaries. This approach enriches the potential for more flexibility and creative form designs.</p>
You don't need JavaScript for that
2023-12-02T00:00:00Z
https://htmhell.dev/adventcalendar/2023/2/
by Kilian Valkhof<br><p>Hello, my dear friend of RSS! This post contains interactive demos. You may want to read it on the website.</p><p>Please don't feel antagonised by the title of this article. I don't hate JavaScript, I love it. I write bucketloads of it every single day. But I also love CSS, and I even love <s>JSX</s> HTML. The reason I love all three of these technologies is something called:</p>
<h2 id="the-rule-of-least-power">The rule of least power</h2>
<p>It's one of the core principles of web development and it means that you should <strong>Choose the least powerful language suitable for a given purpose</strong>.</p>
<p>On the web this means preferring HTML over CSS, and then CSS over JS. JS is the most versatile language out of the three because you're the one describing how the browser should act, but it can also break, it can fail to load and it takes extra resources to download, parse and run. It is also very easy to exclude keyboard users and people using assistive technologies with it.</p>
<p>In contrast to JS, which is imperative, HTML and CSS are declarative. You tell the browser <em>what</em> to do, not <em>how</em> to do it. That means the browser gets to choose how to do it, and it can do it in the most efficient way possible.</p>
<p>Because HTML and CSS features are handled by the browser they can be more performant, more native, more adaptable to user preferences and in general, more accessible. That doesn't mean it will always be (especially when it comes to accessibility) but when the browser does the heavy lifting for you, your end users will generally have a better experience.</p>
<h2 id="but-i-need-js-for-that!">But I need JS for that!</h2>
<p>You might be thinking āAll the things I use JS for, I <em>need</em> JS forā. That might be true, but it's good to know that both browser makers and specification writers have been porting a lot of functionality over to CSS and HTML that up to a few years ago needed JS. And that's what this article is about.</p>
<p>The tricky thing with the web is that once you learn how to build something there is never a reason to have to learn it again. That's the contract we have: the web is backwards compatible. (Very few exceptions apply, but the first web page still runs fine in all modern browsers.)</p>
<p>That also means that the solution you learned once becomes part of your toolbox, and you can keep re-implementing it and everytime it will still work. So the examples I'm going to give below are cool (that's why I'm listing them) but what I want you to take away from this article is that just because you <em>know</em> something needs JavaScript, doesn't mean it still does. You can make better websites if you test those assumptions every now and then.</p>
<h2 id="custom-switches">Custom Switches</h2>
<p>We'll start this article off with something we've all had to implement at some point, custom switches. Instead of using a regular checkbox, the design calls for a nice looking switch. Instead of reaching for a JS solution with divs, onclick handlers and internal state, we're going to make use of a regular checkbox and the <code>:checked</code> pseudo class. Here's ther HTML we're going to use:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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></span><br /><span class="highlight-line"> My awesome feature</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p>There's a label element, and inside it a checkbox. The nice thing about this is that the browser is already doing things for us. Because the input is inside the label, the browser has associated them and now we can click anywhere on the label to toggle the checkbox, no onclick handler in sight. The browser gives us this for free. Feature-wise, we're done.</p>
<div data-demo="A checkbox">
<p>
<label>
<input type="checkbox" />
My awesome feature
</label>
</p>
</div>
<p>Of course, designers may not like the way this looks and we want to create a great looking custom switch. So let's add a bunch of CSS:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">input</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> lightgrey<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1.65rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 2.75rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">vertical-align</span><span class="token punctuation">:</span> middle<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border-radius</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 1px 3px #0003 inset<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> 0.25s linear background<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">input::before</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1.25rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1.25rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border-radius</span><span class="token punctuation">:</span> 1.2rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">top</span><span class="token punctuation">:</span> 0.2rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">left</span><span class="token punctuation">:</span> 0.2rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 1px 3px #0003<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">transition</span><span class="token punctuation">:</span> 0.25s linear transform<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>0rem<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>All the specifics of the styling here don't matter as much, but I want you to take a look at that first rule: <code>appearance: none</code>.</p>
<p>Form elements, along with images, are something called "replaced content". That means they're not really part of your HTML, but supplied by the browser. When the browser renders your HTML and finds replaced content, it leaves a box for it, and then replaces that box with the actual content. This is why, for example, images and form elements can't have pseudo-elements: they get replaced when the browser replaces the entire element.</p>
<p><code>appearance</code> is a way of telling the browser to stop doing that. It tells the browser: "Thanks, but I want to style my own form control". And that then allows is to use the <code>::before</code> pseudo-element. The input itself is now the background of our switch, and the <code>::before</code> pseudo-element is the little dot inside of it that does the toggling.</p>
<style>
.switch {
appearance: none;
position: relative;
display: inline-block;
background: lightgrey;
height: 1.65rem;
width: 2.75rem;
vertical-align: middle;
border-radius: 2rem;
box-shadow: 0px 1px 3px #0003 inset;
transition: 0.25s linear background;
}
.switch::before {
content: "";
display: block;
width: 1.25rem;
height: 1.25rem;
background: #fff;
border-radius: 1.2rem;
position: absolute;
top: 0.2rem;
left: 0.2rem;
box-shadow: 0px 1px 3px #0003;
transition: 0.25s linear transform;
transform: translateX(0rem);
}
.switch2:checked {
background: green;
}
.switch2:checked::before {
transform: translateX(1rem);
}
.switch3:focus-visible {
outline: 2px solid dodgerblue;
outline-offset: 2px;
}
</style>
<div data-demo="Styled custom switch">
<p>
<label>
<input type="checkbox" class="switch switch1" />
My awesome feature
</label>
</p>
</div>
<p>Clicking this still checks and unchecks the checkbox, but because we replaced the element we need to do the work of making that visible ourselves. That's where the <code>:checked</code> pseudo-class comes in:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:checked</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">:checked::before</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>1rem<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>When you click the checkbox, that <code>:checked</code> pseudo-class starts to match and that causes the styling to update.</p>
<div data-demo="Styled custom switch">
<p>
<label>
<input type="checkbox" class="switch switch2" />
My awesome feature
</label>
</p>
</div>
<p>So we have a great looking custom switch using native HTML elements and a bit of CSS, but we're not done yet. While for mouse users it's really clear which form control they're interacting with (since they're pointing at it and clicking), for people using a keyboard that's not so easy.</p>
<p>I'm sure you're familiar with this bit of CSS. To get rid of that ugly, dotted, boxy outline.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">input:focus</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>If you're reading this, know that's not a good idea. But how do we make it look, well, nicer? Here too browsers have updated to make things better for us. The <code>outline</code> now follows the border-radius of an element, and we can also offset it away, or inside of, the element:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">input:focus-visible</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> 2px solid dodgerblue<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">outline-offset</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Now, when a user interacts with an element using the keyboard (you can try pressing the spacebar after clicking it, or tabbing to it), <code>:focus-visible</code> will match (it won't when using a mouse) and they get a good looking, blue outline slightly around the element.</p>
<div data-demo="final custom checkbox">
<p>
<label>
<input type="checkbox" class="switch switch2 switch3" />
My awesome feature
</label>
</p>
</div>
<p>Lastly, I want you to replace that <code>outline: none</code> with something else:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">input:focus</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">outline-color</span><span class="token punctuation">:</span> transparent<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>This will have the same result: Instead of the outline not being visible because it's hidden, it's not visible because it's transparent. For users that have high contrast mode (also called forced colors) turned on however, that outline becomes visible again because in high contrast mode, that transparent color gets replaced with a color the user chose, helping them see what they're interacting with even if they use a mouse.</p>
<p>This article isn't long enough to also go into what forced-colors does but if you want to learn more check out my article <a href="https://polypane.app/forced-colors/">forced colors explained</a>.</p>
<h2 id="datalist-a-native-autosuggest">Datalist, a native autosuggest</h2>
<p>Instead of installing <code>$your-framework-autosuggest</code>, try out datalist in your next project. Datalist is the browsers built-in way of showing a list of options as a user types into an input.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">list</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>frameworks<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>datalist</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>frameworks<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Bootstrap<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Tailwind CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Foundation<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Bulma<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Skeleton<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>datalist</span><span class="token punctuation">></span></span></span></code></pre>
<p>To use it, you add a datalist element with an ID and a set of options to your HTML. Don't worry, the element won't be visible. Then you use the <code>list</code> attribute on an input to associate the two.</p>
<div data-demo="Datalist example">
<input list="frameworks" />
<datalist id="frameworks">
<option>Bootstrap</option>
<option>Tailwind CSS</option>
<option>Foundation</option>
<option>Bulma</option>
<option>Skeleton</option>
</datalist>
</div>
<p>As a user now types into the input, the browser will show the datalist as a dropdown, automatically filtering the options as the user types. Because it's a regular input though, users still have the option to type in their own value. Lastly, they can see all of the options by selecting the input and using the arrow keys to navigate the list, or clicking the dropdown icon the browser adds.</p>
<h2 id="a-color-picker-that-does-more">A color picker that does more</h2>
<p>There are a ton of good looking color pickers out there, with nice canvas UIs and sliders that are built with 100s of lines of JavaScript. But did you know that you can also use a native color picker?</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>color<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> Color <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p>This single line of HTML also give you a color picker with a nice UI, already saving you a bunch of JS. But because we're letting the browser handle it, we actually get more functionality for free. In Chromium browsers, that native color picker also lets you pick a color, not just from your own site but from anywhere on the screen. Pretty neat!</p>
<div data-demo="Native color picker">
<p><label for="color">Color</label><br />
<input type="color" id="color" /></p>
</div>
<p>A quick note here is that even though browsers show a nice color picker, your users might not all be able to use it. So offering a different way of picking a color (like a regular text input) is still a good idea.</p>
<h2 id="accordions">Accordions</h2>
<p>Accordions are a great way of making a page with a lot of content more structured and uncluttered by keeping content out of the way until a user needs it. And browsers give them to you for free with the <code>details</code> and <code>summary</code> elements:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>My accordion<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>My accordion content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="Accordion">
<details>
<summary>My accordion</summary>
<p>My accordion content</p>
</details>
</div>
<p>By default everything inside a <code>details</code> element is hidden except for the <code>summary</code>. Then when a user clicks the <code>summary</code> element the browser will show the rest of the content.</p>
<p>What you'll often see is that one of the accordion items is already open, and the rest are closed. You can do that with the <code>open</code> attribute:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>details</span> <span class="token attr-name">open</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>summary</span><span class="token punctuation">></span></span>My accordion<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>summary</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>My accordion content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>details</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="Accordion that starts open">
<details open="">
<summary>My accordion</summary>
<p>My accordion content</p>
</details>
</div>
<p>If you come from the React world, you might look at this code and think āWell that's great, now it has the <code>open</code> prop and isn't going to close anymoreā but luckily, that's not the case. That <code>open</code> attribute is only the starting state, and will update when a user interacts with the accordion.</p>
<p>When it comes to styling, the <code>details</code> element also has you covered. That little triangle (that your designer will want to replace the instant they see it) is a <code>::marker</code> pseudo-element that you can style:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">summary::marker</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 1.5em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"š¬"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">[open] summary::marker</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 1.5em<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"š"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<style>
details.styled summary::marker {
font-size: 1.5em;
content: "š¬";
}
details.styled[open] summary::marker {
font-size: 1.5em;
content: "š";
}
</style>
<div data-demo="Accordion with styled markers">
<details class="styled">
<summary>My accordion</summary>
<p>My accordion content</p>
</details>
</div>
<p>Keep in mind that changing the content can affect how assistive technologies announce your accordion. Read Manuels article on that here: <a href="https://www.matuzo.at/blog/2023/details-summary">details/summary inconsistencies</a>. In addition, for Safari you'll have to use the <code>::-webkit-details-marker</code> pseudo-element.</p>
<p>The marker pseudo-element can't be styled as extensively as other elements (many CSS properties do not work on it, like positioning it in a completely different place), but you can replace its content, for example with emoji, or set a background color or image and change it's font size.</p>
<p>With the <code>open</code> attribute you can easily give it different styling from the closed state.</p>
<p>Lastly, we want to do something about that summary element. It's clickable, but unlike a link it doesn't get a pointer cursor, and unlike a button it, well, doesn't look like a button. So I think we should add a hover and focus state to it and help our visitors realise that it's clickable:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">summary:hover,<br />summary:focus</span> <span class="token punctuation">{</span><br /><span class="highlight-line"> <span class="token property">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> deeppink<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<style>
details.hoverable summary:hover,
details.hoverable summary:focus {
cursor: pointer;
background: deeppink;
}
</style>
<div data-demo="Accordion with indication on hover">
<details class="hoverable">
<summary>My accordion</summary>
<p>My accordion content</p>
</details>
</div>
<p>I'm sidestepping the āonly links should have pointer cursorsā discussion here, the main point I'm making is that you need to do <em>something</em>.</p>
<h2 id="dialog-modals">Dialog modals</h2>
<p>Sometimes you need to inform the user about something, or ask them something or get them to confirm something. In JavaScript, that's what <code>alert()</code>, <code>prompt()</code> and <code>confirm()</code> do. But they have a pretty big downside: they lock the main thread, meaning your page can't do anything else. They're also browser-native, so you can't style them to work with your design.</p>
<p>Building your own dialog is also asking for trouble: you need to keep the focus inside the dialog for accessibility, announce it's modal-ness, make sure users can't exit it accidentally, and you'll have to fight with whatever chat widget occupied the z-index of 2147483647 (if you know you know).</p>
<p>So that's why browsers now come with a native dialog element:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>This is a pretty dialog<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Close<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dialog</span><span class="token punctuation">></span></span></span></code></pre>
<p>This element isn't shown by default and, for now, this is where I'm going to cheat a little and use JavaScript:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"button"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</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></span><br /><span class="highlight-line"> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"dialog"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">showModal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<div data-demo="A native dialog">
<p><button id="open-dialog1">Open dialog</button></p>
<dialog id="dialog1">
<form method="dialog">
<h3>This is a pretty dialog</h3>
<button type="submit">Close</button>
</form>
</dialog>
</div>
<script>
document.getElementById("open-dialog1").addEventListener("click", () => {
document.getElementById("dialog1").showModal();
});
</script>
<p>Now there's changes in the works that will let you open dialogs without JavaScript, but they're not fully specced yet, let alone implemented. So for now, we need to use JavaScript to open the dialog. But that's it, the rest is all native HTML and CSS.</p>
<p>The dialog element has a <code>showModal()</code> function that it exposes and with it, you open the dialog. This dialog is opened on something called the <code>top-layer</code>, which is a new concept in browsers. For a primer, check out the explainer on MDN: <a href="https://developer.mozilla.org/en-US/docs/Glossary/Top_layer">Top layer</a>.</p>
<p>The top layer is a new layer that's separate from your HTML, and you can "promote" elements to it. That means that elements on the top layer will always be above everything else, regardless of the z-index of an element and stacking context nesting.</p>
<p>Now that it's open though, you might notice that the browser doesn't give you any UI. The dialog is pretty much a div (not a button!) and it's up to you to provide the UI for closing. That's what the form in the code above does. You might've noticed it has a method of "dialog". When this form gets submitted, the browser takes that as a signal to close the dialog again.</p>
<p>With that, you can also create confirmation dialogs by providing two buttons, each with each own values:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dialog</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Tabs or spaces?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wrong<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Tabs<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>correct<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Spaces<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dialog</span><span class="token punctuation">></span></span></span></code></pre>
<p>The button that a user clicked can be found by listening to the <code>close</code> event on the dialog and reading it's 'returnValue' property:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line">dialog<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"close"</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>dialog<span class="token punctuation">.</span>returnValue<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<div data-demo="Dialog with multiple buttons">
<p><button id="open-dialog2">Open dialog</button></p>
<dialog id="dialog2">
<form method="dialog">
<p>Tabs or spaces?</p>
<button type="submit" value="wrong">Tabs</button>
<button type="submit" value="correct">Spaces</button>
</form>
</dialog>
</div>
<script>
document.getElementById("open-dialog2").addEventListener("click", () => {
document.getElementById("dialog2").showModal();
});
</script>
<p>If you have any other form data in there you can also read that as <a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData"><code>formData</code></a>.</p>
<p>Because the dialog is essentially a div as far as styling it concerned, you can style it however you want. The browser will automatically place it in the middle of the screen for you, but everything else is up to you.</p>
<p>Dialog also comes with a new pseudo-element called <code>::backdrop</code>. That's the layer that sits between the dialog and the rest of the page, and you can style it to e.g. dim the rest of the page or otherwise direct a users attention to the dialog. For example, you can overlay a white layer and blur the page:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">dialog::backdrop</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> #fff5<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">backdrop-filter</span><span class="token punctuation">:</span> <span class="token function">blur</span><span class="token punctuation">(</span>4px<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<style>
dialog#dialog3::backdrop {
background: #fff5;
backdrop-filter: blur(4px);
}
</style>
<div data-demo="Dialog with styled backdrop">
<p><button id="open-dialog3">Open dialog</button></p>
<dialog id="dialog3">
<form method="dialog">
<h3>This is a dialog with a styled backdrop</h3>
<button type="submit">Close</button>
</form>
</dialog>
</div>
<script>
document.getElementById("open-dialog3").addEventListener("click", () => {
document.getElementById("dialog3").showModal();
});
</script>
<p>Just like the dialog element itself, the backdrop is positioned by the browser, so you won't need to worry about scrolling, fixed elements and browser resizing. It's all handled for you by the browser.</p>
<h2 id="in-closing">In closing</h2>
<p>I hope you found a few things in this article that made you realise you can use a little bit less javascript in your next project. Whenever you change a known battle-tested implementation to something new it's good to test it, especially when it comes to accessibility, to make sure that you're not excluding anyone.</p>
<p>There are dozens more examples I could've added into this article, here are just some you can look into:</p>
<ul>
<li>Native smooth scrolling with <code>scroll-behavior: smooth</code> (but only when <code>prefers-reduced-motion: no preference</code> matches!),</li>
<li>Native carousels with scroll-snap,</li>
<li>"In-view" elements with <code>position: sticky</code></li>
<li>ā¦Not to name the whole concept of container queries.</li>
</ul>
<p>And if we look into the future, we're getting even more cool things:</p>
<ul>
<li>Scroll driven animations</li>
<li>Masonry layouts without masonry.js but with <code>grid-template-rows: masonry</code></li>
<li>A fully stylable <code>select</code> with the new <code>selectlist</code> element (where you can style each part of a select without destroying all the native functionality it comes with)</li>
<li>The <code>:has()</code> selector that's going to eliminate a whole class of JS selection</li>
</ul>
<p>This article is an adaption of a conference talk I gave that goes into more detail on these and other topics, and you can watch it here: <a href="https://www.youtube.com/watch?v=ZTMUJu26b7Q">Stop Using JavaScript for That: Moving Features from JS to CSS and HTML.</a></p>
<p>So let me re-iterate the main point of this article:</p>
<p>Just because you <em>know</em> something needs JavaScript, doesn't mean it still does. You can make better websites if you test those assumptions every now and then.</p>
The UX of HTML
2023-12-01T00:00:00Z
https://htmhell.dev/adventcalendar/2023/1/
by Vasilis van Gemert<br><p>Recently when I gave a coding assignmentāāāan art directed web page about a fontāāāa student asked: does it have to be <em>semantic and shit?</em> The whole class looked up, curious about the answerāāā<em>please let it be no!</em> I answered that no, it doesnāt have to be semantic and shit, but it does have to be well designed and the user experience should be well considered. Relieved, all of my students agreed. They do care about a good user experience.</p>
<p>The joke here is, of course, that a well considered user experience starts with well considered <abbr title="HyperText Markup Language">HTML</abbr>.</p>
<p>Somehow my students are allergic to semantics and shit. And theyāre not alone. If you look at 99% of all websites in the wild, everybody who worked on them seems to be allergic to semantics and shit. On most websites heading levels are just random numbers, loosely based on font-size. Form fields have no labels. Links and buttons are divs. Itās really pretty bad. So itās not just my students, the whole industry doesnāt understand semantics and shit.</p>
<p>Recently I decided to stop using the word <em>semantics</em>. Instead I talk about the <abbr title="User eXperience">UX</abbr> of <abbr>HTML</abbr>. And all of a sudden my students are not allergic to <abbr>HTML</abbr> anymore but really interested. Instead of explaining the meaning of a certain element, I show them <em>what it does</em>. So we look at <em>what happens</em> when you add a label to an input: The input and the label now form a pair. You can now click on the label to interact with a checkbox. The label will be read out loud when you focus on an input with a screenreader. When you hover over a <em>label,</em> the hover state of the connected <em>input</em> is shown. My students <em>love</em> stuff like that. They care about <abbr>UX</abbr>.</p>
<p>I show them that <span onclick="window.location.href='https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span'" style="text-decoration: underline">a span with an onclick-event</span> might seem to be behaving like a link, but that there are many layers of <abbr>UX</abbr> missing when you look a bit closer: right-click on <span onclick="window.location.href='https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span'" style="text-decoration: underline">this span</span>, and a generic context menu opens up. When on the other hand you right-click on <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a">a proper link like this one</a> a specialised context menu opens, with all kinds of options that are specific to a link. And I show them that proper links show up when you ask your screen reader to list all the links on a page, yet spans with an onclick-event donāt. Moreover, a span doesnāt receive focus when you tab to it. And so on. My students see this and they get it. And they love the fact that by being lazy they get much more result.</p>
<p>So when I teach about <abbr>HTML</abbr> I always start with the elements that are obviously interactive. I show them the multitude of <abbr>UX</abbr> layers of a link, I show them the layers and layers of <abbr>UX</abbr> that are added to a well considered form. I show them what happens on a phone when you use an input with a default text type instead of the proper type of email. They <em>get</em> this, and they want to know more. So I show them more. I show them the <code>required</code> attribute which makes it possible to validate not only form fields, but also fieldsets and forms. They <em>love</em> this stuff.</p>
<p>Then I go on and show the <code>details</code> element with a <code>summary</code>. Whoah! No JavaScript!</p>
<p>These interactive things are the most important parts of <abbr>HTML</abbr>. These are the things that truly break or make a site. If you donāt use these elements properly, many people are actively excluded and the <abbr>UX</abbr> degrades in many ways. The interactive elements are what makes the web the web.</p>
<h2 id="the-old-focus-on-semantics">The old focus on semantics</h2>
<p>Every time I come home from a web conference I hang the lanyard with the badge on a doorknob. There are at least 35 lanyards there. The oldest is more than fifteen years old, the newest just a few months. On most of these conferences there was someone telling us about the importance of using proper <em>semantic <abbr>HTML</abbr></em>. And they <em>all</em> talked about the importance of heading levels. So for <em>at least</em> 15 years weāve been telling the web design and web development community that heading levels are very important. Yet after all these years, and after all these conferences <a href="https://webaim.org/projects/million/#headings">there are almost no websites that do it right</a>.</p>
<p>Later, when <abbr>HTML</abbr>5 was introduced the talks about semantics were still mostly about creating a proper document outline, but this time with sectioning elements in combination with headings. Unfortunately this idea was never properly implemented by browsers. And it turned out that these sectioning elements are very hard to understand. For years Iāve tried to explain the difference between a section and an article, and almost nobody gets it. And of course they donāt, because itās <a href="https://www.smashingmagazine.com/2022/07/article-section-elements-accessibility/">very, very complicated theory</a>. And above all: Thereās no clear <abbr>UX</abbr> feature to point at. The user experience doesnāt change dramatically if you use a section instead of an article. Itās <em>mostly</em> theoretical. So nowadays, next to <em>divitis</em>, we also have <em><a href="https://www.reddit.com/r/web_design/comments/1it5aa/is_sectionitis_the_new_divitis/">sectionitis</a>.</em></p>
<p>Understanding how heading levels work is hard. And understanding how sectioning elements work is really hard. And explaining these things is even harder. Whatās the use for <a href="https://www.w3.org/TR/HTML-design-principles/#priority-of-constituencies">theoretical purity</a> if the end result is the same?</p>
<p>Yes, I know that some sectioning elements actually have some <abbr>UX</abbr> attached to it. But not that much <abbr>UX</abbr> if you compare it to the real interactive elements. Not getting your heading levels right is not at all as destructive as using divs instead of links.</p>
<p>Now, Iām not saying that we should stop teaching people about heading levels and sections. We shouldnāt. Heading levels and sections do things as well. But we should think about <em>when</em> we teach these more complicated and subtle parts of <abbr>HTML</abbr>. First we need to get people exited about <abbr>HTML</abbr> by showing all the free yet complex layers of <abbr>UX</abbr> you get when you use the interactive elements properly. And then, when they do understand the interactive elements, when theyāre really excited and they ask for more, show them the more obscure <abbr>UX</abbr> patterns. You need a good idea of what <abbr>UX</abbr> <em>is</em> before you can understand things like <a href="https://webaim.org/projects/screenreadersurvey9/#finding">the option to nagivate through the headings on the page</a> with a screen reader. Without an idea of what <abbr>UX</abbr> means you cannot understand what landmarks do. First start with the obvious, then show the details.</p>
<p>I am very much looking forward to 15 more years of web conferences and publications. I look forward to seeing inspiring talks about the <abbr>UX</abbr> of <abbr>HTML</abbr>. Talks about the incredible radio button and its wonderful <em>indeterminate</em> state! Talks about validating forms in a friendly way with just <abbr>HTML</abbr> and some clever <abbr title="Cascading StyleSheets">CSS</abbr>. Talks about the different context menus that appear on different elements. In other words, talks about what <abbr>HTML</abbr> <em>does</em>, and much less about what it <em>means in theory</em>. Letās talk about user experience, and letās stop talking about semantics and shit.</p>
Preventing form submission with zero Javascript
2022-12-24T00:00:00Z
https://htmhell.dev/adventcalendar/2022/24/
<p>Want to <a href="https://ashleemboyer.com/blog/should-i-use-a-button-or-a-link">trigger an action</a>? Use <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button">a button element</a>. Theyāre great.</p>
<p>Want to also prevent form submission when someone clicks that button? Put down the JavaScript, friend. I have a better suggestion:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Button action goes here</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>And thatās it! No <code>preventDefault()</code> or no overwrought dependencies that will stop working without notice. Just good olā reliable HTML and <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type">a humble attribute</a>.</p>
<p>Okay, cool, thanks!</p>
<p>PS from Manuel: Thank you so much to all authors for this wonderufl series of articles and thank you to all readers for reading, sharing, and commenting! Merry christmas and a happy new year! š¤</p>
Enforcing better HTML markup with Eleventy
2022-12-23T00:00:00Z
https://htmhell.dev/adventcalendar/2022/23/
<p>While what we mean is usually very clear to us, others may decode our messages differently from what we intended. This is especially true on the web, where there are many ways to consume content. The language, browser type, device model, using a screen reader, navigating with or without a mouse - all of these factors can influence how information is experienced on the web.</p>
<p>And the glue that can better aid all these factors might be developing with semantic HTML which helps in describing content more accurately. In this article, we will explore common issues related to incorrect HTML markup and how you can enforce good practices to fix them.</p>
<h2 id="introduction">Introduction</h2>
<p>Web experiences should be fast, accessible, and tailored to the capabilities of users and the devices they own. A premise we could all agree on, as we have all have suffered from poorly designed web experiences. And if you are a web developer, you might have once followed practices that led to such experiences.</p>
<p>Still, building for a wide range of users you know nothing about is far from being a straightforward process. Fortunately, most of the research have extensively been done and made available sets of proven practices that when followed can ensure that what we build does not exclude groups of people arbitrarily. So how can we develop interfaces that create such experiences? To first understand that, we have to understand the role of semantics.</p>
<h3 id="semantics-in-spoken-languages">Semantics in spoken languages</h3>
<p>Something we usually agree on in society is how important it is to define things. Without mutual understanding, we wouldn't be able to communicate effectively.</p>
<p>If I ask you to define what a cup of coffee is, you might provide an answer resembling: ā<em>a drink made from coffee beansā</em>, whereas <a href="https://www.urbandictionary.com/define.php?term=coffee">the internet</a> might go for <em>āthe juice of a plant that gives life and suppresses the urge for murderā</em>, or you might have your own definition not too far from these two. This mutual understanding of different concepts helps to communicate with each other. Whenever you buy a cup of coffee, you will expect what you receive to reflect this definition. You can even go a step further and adjust your words based on who you are talking to and how that person might feel at the moment.</p>
<p>For instance, you could find yourself speaking louder to someone hard of hearing, avoid technical words with a young person, or even adding gestures to illustrate what would otherwise be too abstract. Such adjustments are done unconsciously and aim to increase the likelihood that every exchange is done efficiently. The last thing you want is to have to persistently repeat something over and over to a person who simply does not understand what you are saying instead of adjusting once.</p>
<h3 id="semantics-on-the-web">Semantics on the web</h3>
<p>So what about on the web? Are semantics still as important and is it possible to adjust our message to the circumstances of others?</p>
<p>Communicating did indeed go past the face-to-face interactions and has reached the internet, in the form of blogs, websites, or social media. Even if the medium differs, I would argue that the idea of helping others better understand your content remains the same. Whether you are a company seeking profit through selling a product, a creator promoting his content or someone who occasionally post blog articles, there is surely a benefit in having a more reachable content. In that aspect, the need for semantics on the web is very close to that of spoken languages.</p>
<blockquote>Specifically, for people with disabilities, they point assistive technology at what stuff is, so there is a bit of responsibility, a big responsibility that we have as Web developers, to make sure we point machines and assistive tech into the right direction and show them exactly what stuff is and what we mean by stuff in our user interfaces.</blockquote>
<p>(Hidde de Vries, <a href="https://youtu.be/YG9WQUfH7ZU?t=981">Semantics and How to Get Them Right</a>)</p>
<h4>Taking accountability as a web developer</h4>
<blockquote>Harry Potter and the Sorcerer's Stone (2001).<br />
An orphaned boy enrolls in a school of wizardry, where he learns the truth about himself, his family and the terrible evil that haunts the magical world.</blockquote>
<p>Without context you might have understood from this small excerpt that ā<em>Harry Potter and the Sorcerer's Stoneā</em> corresponds to the title of a media published in ā<em>2001ā</em>. Thatās the kind of extra meaning you can explicitly attach to content on the web through HTML elements and attributes.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Harry Potter and the Sorcerer's Stone<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2001<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2001<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>An orphaned boy enrolls in a school of wizardry, where he learns the truth about himself, his family and the terrible evil that haunts the magical world.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<p>This would then be conveyed as if you were saying āHey device, present ā<em>Harry Potter and the Sorcerer's Stoneā</em> as the title, this number value as the year of a date, and the remaining text as a paragraph.ā By understanding the inherent meaning of such content, devices are able to convey it more accurately to a wide number of people, regardless of how they decide to experience it.</p>
<p>As someone responsible for building interfaces, not only are you trying to make your content understandable, you also have to make sure that content is accurately conveyed to their devices.</p>
<h4>Helping users create their own ideal experience</h4>
<p>In reality how does this help? In the same way that someone hard of hearing would have difficulties understanding what you say, there are situations that can prevent users from effectively consuming content.</p>
<p>And because you canāt adjust in real time, the idea is to provide all the hints you can to help users help themselves. Maybe the person browsing has a limited monthly internet package, and would sometimes prefer a <a href="https://blog.jim-nielsen.com/2022/website-fidelity/">low fidelity version of your website</a> with only text content? Or that person has low vision, and would benefit from reading bigger text sizes? Perhaps said person is currently browsing while engaging in a phone call and could benefit from only using the keyboard to navigate? Such circumstances are intrinsic to the person, but often are reflected to their devices through preferences or apps aimed at supporting them.</p>
<p>As a developer, the HTML markup you write leads to an interface that users can view on their devices. Not only are your users diverse, but so are the devices they use. You may write sentences that are semantically valid, but the information may not be correctly displayed by your interface if you do not <a href="https://web.dev/learn/html/semantic-html/">develop semantically</a>. By utilizing the appropriate HTML elements to wrap your content, you can provide cues to devices browsing your interface about the way your content should be described.</p>
<p>When you start from scratch, it can be a tedious process to write good semantic HTML. As the complexity of your project increases, you may end up writing the same HTML patterns over and over again.</p>
<p>The sooner you can identify these patterns and establish a system for reusing them, the easier the development process will be. Having highlighted the role of semantics, let's look at the most common problems related to HTML markup and how you can avoid them.</p>
<h2 id="common-html-markup-issues">Common HTML markup issues</h2>
<p>Here are common issues encountered on the web that stems from bad HTML markup.</p>
<ul>
<li>Each form field is not associated with its own label.</li>
<li>The heading structure is not respected.</li>
<li>Missing alternative text on images.</li>
<li>The opening HTML tag does not specify the main language.</li>
<li>The HTML code is not valid.</li>
</ul>
<h3 id="sources">Sources</h3>
<ul>
<li><a href="https://darekkay.com/blog/evaluatory-personal-sites/#results">I've analyzed the accessibility of over 1600 personal sites</a></li>
<li><a href="https://hidde.blog/common-a11y-issues/">Common accessibility issues that you can fix today</a></li>
<li><a href="https://hidde.blog/more-common-a11y-issues/#heading-3">More common accessibility issues that you can fix today</a></li>
</ul>
<h2 id="designing-a-system-for-writing-better-html-in-eleventy">Designing a system for writing better HTML in Eleventy</h2>
<blockquote>Solid, accessible HTML should be the base of any web stack. If everything fails (and it will), your users still get the core content.</blockquote>
<p>(<a href="https://www.trysmudford.com/blog/uniting-the-team-with-jamstack/">Trys Mudford, Unyfying with Jamstack</a>)</p>
<p>We are trying to implement a low configuration system that outputs HTML without adding a huge library or framework around it. We can achieve this by using a <a href="https://en.wikipedia.org/wiki/Static_site_generator">static site generator</a> for addressing the need for reusable patterns. With the right foundations, it can support the implementation of robust and accessible HTML markup. Here we will explore how <a href="https://www.11ty.dev/">Eleventy</a>, a static site generator, can help us in this regard.</p>
<p>Eleventy provides multiples ways to create <a href="https://fuzzylogic.me/posts/reusable-code-snippets-and-components-in-eleventy/">reusable components</a>. We will implement patterns that follow good practices and, by using them consistently we can ensure that the overall project does not deviate from our goal.</p>
<h2 id="enforcing-better-html-markup-in-eleventy">Enforcing better HTML markup in Eleventy</h2>
<blockquote>When you design a system, or a language, then if the features can be broken into relatively loosely bound groups of relatively closely bound features, then that division is a good thing to be made a part of the design. This is just good engineering.</blockquote>
<p><a href="https://www.w3.org/DesignIssues/Principles.html">Tim Berners Lee, Principles of Design</a></p>
<h3 id="reusable-components-with-nunjucks-macros">Reusable components with Nunjucks macros</h3>
<p><a href="https://cloudcannon.com/community/learn/eleventy-beginner-tutorial/templating-in-eleventy/">Templating in Eleventy</a> is what allows us to control how pages are rendered. It makes it possible to generate variables, loop over an array or run a custom plugin to generate data. Eleventy can handle a dozen different language models such as Nunjucks which we are going to use.</p>
<p><strong>Nunjucks macros</strong></p>
<p>Nunjucks has this feature called <a href="https://iainbean.com/posts/2020/flexible-components-in-eleventy-with-nunjucks-macros/">macros</a> that hit all the boxes for building reusable components. It is similar to a function in a programming language and accepts arguments. The method for defining components macro is inspired by Trys Mudford <a href="https://www.trysmudford.com/blog/encapsulated-11ty-components/">detailed article</a> on the subject.</p>
<p>For instance, we could create a button macro that takes in a label and output the markup for a tag component.</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token delimiter punctuation">{%</span> <span class="token tag keyword">macro</span> <span class="token function">button</span><span class="token punctuation">(</span><span class="token variable">label</span><span class="token punctuation">,</span> <span class="token variable">type</span><span class="token operator">=</span><span class="token string">"button"</span><span class="token punctuation">,</span> <span class="token variable">status</span><span class="token operator">=</span><span class="token string">'success'</span><span class="token punctuation">)</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">button</span> <span class="token variable">type</span><span class="token operator">=</span><span class="token string">"{{type}}"</span> <span class="token variable">class</span><span class="token operator">=</span><span class="token string">"tag-{{status}}"</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token punctuation">{</span><span class="token variable">label</span><span class="token punctuation">}</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token operator">/</span><span class="token variable">button</span><span class="token operator">></span></span><br /><span class="highlight-line"><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endmacro</span> <span class="token delimiter punctuation">%}</span></span></code></pre>
<p>Now <code>button</code> is available to be called like a normal function:</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token delimiter punctuation">{{</span> <span class="token function">button</span><span class="token punctuation">(</span><span class="token variable">label</span><span class="token operator">=</span><span class="token string">'Disconnect'</span><span class="token punctuation">,</span> <span class="token variable">status</span><span class="token operator">=</span><span class="token string">'alert'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">{</span># <span class="token variable">Output</span> #<span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token variable">button</span> <span class="token variable">type</span><span class="token operator">=</span><span class="token string">"button"</span> <span class="token variable">class</span><span class="token operator">=</span><span class="token string">"tag-alert"</span><span class="token operator">></span><span class="token variable">Disconnect</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">button</span><span class="token operator">></span></span></code></pre>
<p>The only catch is that all macro parameters are optional by default and not providing one will not prevent our component from being rendered. Since we try to enforce specific parameters within our components to make them more accessible, we would rather be informed whenever a critical parameter is not provided. So how can we achieve this?</p>
<p><strong>Building filter to enforce required parameters inside macros</strong></p>
<p>We can take advantage of another Eleventy feature to do this, namely <a href="https://www.11ty.dev/docs/filters/">filters</a>. In Eleventy, filters are JavaScript functions that accept content, modify it, and then return it to be displayed instead of the original.</p>
<p>This sounds a lot like macros, you might say, and that would be true. The only difference is that filters are best used to transform data rather than generate snippets of content.</p>
<p>We can start by creating a filter that throws a generic error when the value it receives is <code>undefined</code>.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="highlight-line">eleventyConfig<span class="token punctuation">.</span><span class="token function">addFilter</span><span class="token punctuation">(</span><span class="token string">"enforced"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span><span class="token punctuation">(</span>value <span class="token operator">===</span> <span class="token keyword">undefined</span><span class="token punctuation">)</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">"A parameter is missing"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token keyword">return</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>By applying this filter to the parameters of the macros we can make them mandatory.</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token delimiter punctuation">{%</span> <span class="token tag keyword">macro</span> <span class="token function">button</span><span class="token punctuation">(</span><span class="token variable">label</span><span class="token punctuation">,</span><span class="token variable">type</span><span class="token operator">=</span><span class="token string">"button"</span><span class="token punctuation">,</span> <span class="token variable">status</span><span class="token operator">=</span><span class="token string">'success'</span><span class="token punctuation">)</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">button</span> <span class="token variable">type</span><span class="token operator">=</span><span class="token string">"{{ type }}"</span> <span class="token variable">class</span><span class="token operator">=</span><span class="token string">"tag-{{ status }}"</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token variable">label</span> <span class="token operator">|</span> <span class="token variable">enforced</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token operator">/</span><span class="token variable">button</span><span class="token operator">></span></span><br /><span class="highlight-line"><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endmacro</span> <span class="token delimiter punctuation">%}</span></span></code></pre>
<p>We can then use it inside our component definition to flag parameters we want as required.</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token operator"><</span><span class="token variable">button</span> <span class="token variable">type</span><span class="token operator">=</span><span class="token string">"{{ type }}"</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token variable">label</span> <span class="token operator">|</span> <span class="token variable">enforced</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">button</span><span class="token operator">></span></span></code></pre>
<p>Using this button component without providing a label parameter would throw an error which is what we want.</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token punctuation">{</span># <span class="token variable">Throws</span> <span class="token variable">an</span> <span class="token variable">error</span> <span class="token variable">because</span> <span class="token variable">the</span> <span class="token variable">label</span> <span class="token keyword">is</span> <span class="token keyword">not</span> <span class="token test function">provided</span> #<span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token function">button</span><span class="token punctuation">(</span><span class="token variable">status</span><span class="token operator">=</span><span class="token string">'alert'</span><span class="token punctuation">)</span> <span class="token delimiter punctuation">}}</span></span></code></pre>
<p>And now, by combining Nunjucks macros and a filter, we can create reusable components and set up rules to make sure they meet our criteria. <a href="https://frontendchecklist.io/">The Front End Checklist</a> or the <a href="https://checklists.opquast.com/en/web-quality-assurance/">Web Quality Assurance Checklist</a> are both great resources to find ideas of criteria for writing better HTML markup. In the following sections we will address common HTML markup issues by utilizing this method.</p>
<h3 id="how-to-enforce-labels-inside-form-fields?">How to enforce labels inside form fields?</h3>
<p>To enable users with disabilities to access form fields, it is essential to identify them. That way, screen reader users could scan the form fields by name or use keyboard shortcuts to quickly navigate from one field to another.</p>
<p><strong>Criteria</strong></p>
<ul>
<li>A <code>label</code> must be associated with each input form element.</li>
<li>Asterisks should not be used to mark required fields. <a href="https://www.semanticscholar.org/paper/The-Privacy-Economics-of-Voluntary-Over-disclosure-Preibusch-Krol/3f9426bf2c0b1808db4b5ce940ed36f476bc6d61">Research suggests</a> that they're not necessary and they can make users anxious and less likely to complete the form. Instead, the word <code>optional</code> should be added to non-required fields.</li>
<li>For security reasons, the <a href="https://www.aaron-gustafson.com/notebook/spellcheckers-exfiltrating-pii_-not-so-fast/">spellcheck attribute should be set to false for password fields</a>.</li>
</ul>
<p>These criteria would lead to the following result for a password field.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Password</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>password<span class="token punctuation">"</span></span> <span class="token attr-name">spellcheck</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 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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<span class="token punctuation">"</span></span> <span class="token attr-name">required</span><span class="token punctuation">></span></span></span></code></pre>
<p>The associated component could be built using the following Nunjucks macro:</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token operator"><</span><span class="token variable">label</span> <span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"{{ inputId | enforced }}"</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token variable">label</span> <span class="token operator">|</span> <span class="token variable">enforced</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span> <span class="token keyword">if</span> <span class="token keyword">not</span> <span class="token variable">required</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">span</span> <span class="token variable">aria</span><span class="token operator">-</span><span class="token variable">hidden</span><span class="token operator">=</span><span class="token string">"true"</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token variable">Optional</span><span class="token punctuation">)</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">span</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endif</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">label</span><span class="token operator">></span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token variable">input</span> </span><br /><span class="highlight-line"> <span class="token variable">type</span><span class="token operator">=</span><span class="token string">"{{ inputType or 'text' }}"</span> </span><br /><span class="highlight-line"> <span class="token variable">id</span><span class="token operator">=</span><span class="token string">"{{ inputId | enforced }}"</span> </span><br /><span class="highlight-line"> <span class="token variable">name</span><span class="token operator">=</span><span class="token string">"{{ inputName | enforced }}"</span> </span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span> <span class="token keyword">if</span> <span class="token variable">spellcheck</span><span class="token operator">%</span><span class="token punctuation">}</span> <span class="token variable">spellcheck</span><span class="token operator">=</span><span class="token string">"{{ spellcheck}}"</span> <span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endif</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span> <span class="token keyword">if</span> <span class="token variable">required</span> <span class="token operator">%</span><span class="token punctuation">}</span> <span class="token variable">required</span> <span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endif</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token operator">></span></span></code></pre>
<p><strong>Sources</strong></p>
<ul>
<li><a href="https://www.ovl.design/text/inclusive-inputs/">How to make inputs more accessible</a></li>
<li><a href="https://www.smashingmagazine.com/printed-books/form-design-patterns/">Form Design Patterns</a></li>
<li><a href="https://www.lukew.com/ff/entry.asp?725">Marking Required vs. Optional form fields</a></li>
<li><a href="https://www.aaron-gustafson.com/notebook/spellcheckers-exfiltrating-pii_-not-so-fast/">Spellcheckers exfiltrating PII⦠not so fast</a></li>
</ul>
<h3 id="how-to-allow-flexible-headings-inside-components?">How to allow flexible headings inside components?</h3>
<blockquote>For screen reader users, headings describe the relationships between sections and subsections and ā where used correctly ā provide both an outline and a means of navigation.</blockquote>
<p><a href="https://medium.com/@Heydon/managing-heading-levels-in-design-systems-18be9a746fa3">Heydon Pickering</a></p>
<p>A common challenge you may face when building with a component-first approach is to settle for a single heading level. Yet, components should not be tied to a single situation, otherwise they will lose their primary benefit of being reusable. As such, it would not be appropriate to specify in advance a heading level which you cannot change according to the context.</p>
<p>Having a flexible way of setting the heading of a component can make it easier to respect correct heading structures while keeping the benefits of components. We can achieve that flexibility by passing the heading element as a parameter inside our macro and it just works.</p>
<p>Here we will build a post card component. The heading level should be provided depending on the context.</p>
<p><strong>Criteria</strong></p>
<ul>
<li>Headings should be flexible</li>
</ul>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token operator"><</span><span class="token variable">article</span> <span class="token variable">class</span><span class="token operator">=</span><span class="token string">"card"</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span><span class="token keyword">if</span> <span class="token variable">imageUrl</span><span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">img</span> <span class="token variable">src</span><span class="token operator">=</span><span class="token string">"{{imageUrl}}"</span> <span class="token variable">alt</span><span class="token operator">=</span><span class="token string">"{{imageAlt | enforced}}"</span><span class="token operator">/</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span><span class="token variable">endif</span><span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token punctuation">{</span><span class="token punctuation">{</span><span class="token variable">headingElement</span> <span class="token operator">|</span> <span class="token variable">enforced</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">a</span> <span class="token variable">href</span><span class="token operator">=</span><span class="token string">"{{ url | enforced }}"</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token variable">title</span> <span class="token operator">|</span> <span class="token variable">enforced</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token operator">/</span><span class="token variable">a</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token operator">/</span><span class="token punctuation">{</span><span class="token punctuation">{</span><span class="token variable">headingElement</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span><span class="token keyword">if</span> <span class="token variable">description</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">p</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token punctuation">{</span><span class="token variable">description</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">p</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span><span class="token variable">endif</span><span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span><span class="token keyword">if</span> <span class="token variable">time</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">time</span> <span class="token variable">datetime</span><span class="token operator">=</span><span class="token string">"{{time}}"</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token variable">time</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">time</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">endif</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">article</span><span class="token operator">></span></span></code></pre>
<p>Inside a template we can use the card component while passing the correct heading element.</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token operator"><</span><span class="token variable">section</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">h2</span><span class="token operator">></span><span class="token variable">Card</span> <span class="token variable">components</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">h2</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token function">component</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token string">"card"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token variable">title</span><span class="token punctuation">:</span> <span class="token string">"About"</span><span class="token punctuation">,</span> <span class="token variable">url</span><span class="token punctuation">:</span> <span class="token string">"/about"</span><span class="token punctuation">,</span> <span class="token variable">headingElement</span><span class="token punctuation">:</span> <span class="token string">"h3"</span><span class="token punctuation">,</span> <span class="token variable">description</span><span class="token punctuation">:</span> <span class="token string">"Links and attributions"</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">section</span><span class="token operator">></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token punctuation">{</span># <span class="token variable">Output</span> #<span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token variable">section</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">h2</span><span class="token operator">></span><span class="token variable">Card</span> <span class="token variable">components</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">h2</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">article</span> <span class="token variable">class</span><span class="token operator">=</span><span class="token string">"card"</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">h3</span><span class="token operator">></span><span class="token operator"><</span><span class="token variable">a</span> <span class="token variable">href</span><span class="token operator">=</span><span class="token string">"/about"</span><span class="token operator">></span><span class="token variable">About</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">a</span><span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">h3</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">p</span><span class="token operator">></span><span class="token variable">Links</span> <span class="token keyword">and</span> <span class="token variable">attributions</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">p</span><span class="token operator">></span> </span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token operator">/</span><span class="token variable">article</span><span class="token operator">></span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">section</span><span class="token operator">></span></span><br /><span class="highlight-line"><span class="token punctuation">{</span># <span class="token variable">Output</span> #<span class="token punctuation">}</span></span></code></pre>
<p><strong><strong><strong><strong><strong><strong><strong>Sources</strong></strong></strong></strong></strong></strong></strong></p>
<ul>
<li><a href="https://inclusive-components.design/cards/">Inclusive Components - Cards</a></li>
<li><a href="https://iainbean.com/posts/2020/flexible-components-in-eleventy-with-nunjucks-macros/">Flexible components in Eleventy with Nunjucks macros</a></li>
<li><a href="https://medium.com/@Heydon/managing-heading-levels-in-design-systems-18be9a746fa3">Managing Heading Levels In Design Systems</a></li>
</ul>
<h3 id="how-to-enforce-missing-alternative-text-on-images?">How to enforce missing alternative text on images?</h3>
<p>In the context of visuals, alternative text is a textual description for people who cannot process information visually. In an effort to maintain a comparable experience for everyone, we can add these descriptions to visual contents. If you are interested you can find a thorough guide on <a href="https://uxdesign.cc/how-to-write-an-image-description-2f30d3bf5546">how to write an image description</a>. If the image is purely <a href="https://www.w3.org/WAI/tutorials/images/decorative/">decorative</a>, a <code>null</code> (empty) <code>alt</code> text should be provided (<code>alt=""</code>) so that they can be ignored by assistive technologies, such as screen readers.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>horizontal-wiggly-line.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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>In the post card component created previously, an image is included to illustrate posts. By using the <code>enforced</code> filter we can enforce an alternative text on these images.</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token delimiter punctuation">{%</span><span class="token tag keyword">if</span> <span class="token variable">imageUrl</span><span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">img</span> <span class="token variable">src</span><span class="token operator">=</span><span class="token string">"{{imageUrl}}"</span> <span class="token variable">alt</span><span class="token operator">=</span><span class="token string">"{{imageAlt | enforced}}"</span><span class="token operator">/</span><span class="token operator">></span></span><br /><span class="highlight-line"><span class="token punctuation">{</span><span class="token operator">%</span><span class="token variable">endif</span><span class="token delimiter punctuation">%}</span></span></code></pre>
<p><strong>Separation of concerns</strong></p>
<p>In Nunjucks you define variables with the <code>set</code> keyword. For a more convenient use, we unpack each arguments from the <code>params</code> object.</p>
<p>As shown in the following code snippet we could retrieve the value and enforce it at the top of the macros.</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token delimiter punctuation">{%</span> <span class="token tag keyword">set</span> <span class="token variable">imageAlt</span> <span class="token operator">=</span> <span class="token variable">params</span><span class="token punctuation">.</span><span class="token variable">imageAlt</span> <span class="token operator">|</span> <span class="token variable">enforced</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">set</span> <span class="token variable">url</span> <span class="token operator">=</span> <span class="token variable">params</span><span class="token punctuation">.</span><span class="token variable">url</span> <span class="token operator">|</span> <span class="token variable">enforced</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">set</span> <span class="token variable">headingElement</span> <span class="token operator">=</span> <span class="token variable">params</span><span class="token punctuation">.</span><span class="token variable">headingElement</span> <span class="token operator">|</span> <span class="token variable">enforced</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">set</span> <span class="token variable">title</span> <span class="token operator">=</span> <span class="token variable">params</span><span class="token punctuation">.</span><span class="token variable">title</span><span class="token operator">|</span> <span class="token variable">enforced</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">set</span> <span class="token variable">imageUrl</span> <span class="token operator">=</span> <span class="token variable">params</span><span class="token punctuation">.</span><span class="token variable">imageUrl</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token variable">article</span><span class="token operator">></span> </span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span><span class="token keyword">if</span> <span class="token variable">imageUrl</span><span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">img</span> <span class="token variable">src</span><span class="token operator">=</span><span class="token string">"{{imageUrl}}"</span> <span class="token variable">alt</span><span class="token operator">=</span><span class="token string">"{{imageAlt}}"</span><span class="token operator">/</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span><span class="token variable">endif</span><span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span>!<span class="token operator">-</span><span class="token operator">-</span> <span class="token operator">-</span><span class="token operator">-</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token punctuation">{</span><span class="token punctuation">{</span><span class="token variable">headingElement</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">a</span> <span class="token variable">href</span><span class="token operator">=</span><span class="token string">"{{ url }}"</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token variable">title</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token operator">/</span><span class="token variable">a</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token operator">/</span><span class="token punctuation">{</span><span class="token punctuation">{</span><span class="token variable">headingElement</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span><span class="token keyword">if</span> <span class="token variable">description</span> <span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">p</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token punctuation">{</span><span class="token variable">description</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">p</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token operator">%</span><span class="token variable">endif</span><span class="token operator">%</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">article</span><span class="token operator">></span></span></code></pre>
<p>This separation enables us to separate the logic from the template.</p>
<p><strong><strong><strong><strong><strong><strong><strong><strong><strong>Addressing redundant content</strong></strong></strong></strong></strong></strong></strong></strong></strong></p>
<p>In a card, the alternative text is supposed to describe the content of the image, and that content usually refers to the same content. As a result, the alternative text might be similar to the heading, which would create a duplicate. To prevent this, we may want to include more logic that detects a matching alternative text to the heading and throws out an error. First, we have to improve the filter so it throws an error when the <code>force</code> is set to <code>true</code>.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="highlight-line">eleventyConfig<span class="token punctuation">.</span><span class="token function">addNunjucksFilter</span><span class="token punctuation">(</span><span class="token string">"enforced"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">value<span class="token punctuation">,</span> force</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span><span class="token punctuation">(</span>value <span class="token operator">===</span> <span class="token keyword">undefined</span> <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">"A parameter is missing"</span><span class="token punctuation">)</span> </span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span><span class="token punctuation">(</span> force <span class="token operator">===</span> <span class="token boolean">true</span> <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">An error occured: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>value<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token keyword">return</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>The inside the macro component we can throw an error if the heading title is similar to the image description.</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token punctuation">{</span># <span class="token variable">Advanced</span> #<span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">{</span><span class="token operator">%</span> <span class="token variable">set</span> <span class="token variable">check_redundancy</span> <span class="token operator">=</span> <span class="token string">"The title is redundant with alt text"</span> <span class="token operator">|</span> <span class="token function">enforced</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token variable">title</span> <span class="token operator">==</span><span class="token operator">=</span> <span class="token variable">imageAlt</span> <span class="token delimiter punctuation">%}</span></span></code></pre>
<h3 id="how-to-enforce-the-language-of-the-page?">How to enforce the language of the page?</h3>
<p>Specifying the language will enable text to speech tools to pronounce the content with the appropriate accent for the language used. It can also help translation tools. At the root of the project we can create a <code>_data</code> folder that will hold <a href="https://11ty.recipes/recipes/add-global-data/">Eleventy global data files</a>. Global data files allow you to store data in a single file and reference it in all of your templates.</p>
<p>By creating a global data file we can apply the correct language attribute across the entire project. Here we are adding this attribute inside a <code>site.js</code> global data file.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="highlight-line">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> title<span class="token operator">:</span> <span class="token string">"HTML Practices"</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> description<span class="token operator">:</span> <span class="token string">"Enforcing HTML best practices in Eleventy"</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> language<span class="token operator">:</span> <span class="token string">"en"</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>We can the reference the language attribute in the HTML opening tag of templates.</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="highlight-line"><span class="token operator"><</span>!<span class="token variable">DOCTYPE</span> <span class="token variable">html</span><span class="token operator">></span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token variable">html</span> <span class="token variable">lang</span><span class="token operator">=</span><span class="token string">"{{site.language }}"</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">head</span><span class="token operator">></span><span class="token operator"><</span>!<span class="token operator">-</span><span class="token operator">-</span> <span class="token variable">Head</span> <span class="token variable">content</span> <span class="token operator">-</span><span class="token operator">-</span><span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">head</span><span class="token operator">></span></span><br /><span class="highlight-line"> <span class="token operator"><</span><span class="token variable">body</span><span class="token operator">></span><span class="token operator"><</span>!<span class="token operator">-</span><span class="token operator">-</span> <span class="token variable">Body</span> <span class="token variable">content</span> <span class="token operator">-</span><span class="token operator">-</span><span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">body</span><span class="token operator">></span></span><br /><span class="highlight-line"><span class="token operator"><</span><span class="token operator">/</span><span class="token variable">html</span><span class="token operator">></span></span></code></pre>
<h3 id="validating-the-html-code">Validating the HTML code</h3>
<p>There is a limit to what you can achieve by manually testing your websites. And even by <em>āenforcingā</em> good practices into your workflow, you are bound to miss something once in a while. To limit such occurrences you can use an automated tool for validating HTML markup. Inside Eleventy we can use <a href="https://darekkay.com/evaluatory/">Evaluatory</a> , an open-source tool for website validation. It tests a website for accessibility and markup issues. We can <a href="https://matthiasott.com/notes/generating-accessibility-test-results-with-evaluatory">use Evaluatory to automate checks on save</a> inside our project.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Writing good HTML markup is one step towards providing a better web experience. And as long as this foundation is sturdy, you can effectively provide a better experience overall. You can explore this <a href="https://github.com/nelioyann/enforcing-html-11ty">Github repository</a> to find all the implementations mentioned in this article.</p>
What is the Difference Between Alternative Text, Long Description, and Caption?
2022-12-22T00:00:00Z
https://htmhell.dev/adventcalendar/2022/22/
<p>When it comes to adding images on the web, you need to consider how to make them accessible and understandable to everyone.</p>
<p>Which means you need to include text alternatives to describe the image information or function.</p>
<p>Three options are:</p>
<ol>
<li>Alternative Text</li>
<li>Long description</li>
<li>Caption</li>
</ol>
<p>But how do you know which one to use? And when? Here's info to help you make that decision.</p>
<h2 id="alternative-text">Alternative Text</h2>
<p><strong>Alternative text is a concise image text alternative</strong> for screen reader users. It's not shown to sighted users unless the image doesn't load.</p>
<p>Which can happen when someone has turned off images on their email application. Or they're using a slow connection.</p>
<div data-demo="Example code">
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>flickr.svg<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>Flickr<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
</div>
<h2 id="long-description">Long Description</h2>
<p><strong>Long descriptions are longer text alternatives</strong> for <a href="https://www.w3.org/WAI/tutorials/images/complex/">complex images</a>, which are shown to both sighted and screen reader users.</p>
<p>An expand/collapse accordion below an image is one example of a long description. Another example is a text description below the image.</p>
<p>Note: don't confuse the <code>longdesc</code> attribute with long description. <a href="https://caniuse.com/?search=longdesc">Longdesc is a deprecated/obsolete</a> and should no longer be used. It doesn't work on VoiceOver or mobile screen readers.</p>
<div data-demo="Example code">
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>remote-workforce-infographic.jpg<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>The Benefits of a Remote Workforce infographic with text description below<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Text Version<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<h2 id="caption">Caption</h2>
<p><strong>An image caption is visible to all users</strong> and typically displays below the image.</p>
<p>It's used to convey additional information about the image; it doesn't describe the image directly.</p>
<p>Image captions are optional and <strong>should never be the same as alternative text</strong>.</p>
<div data-demo="Example code">
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figure</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>figure<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>figcaption<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>detroit-parade-clown.jpg<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>Clown with bright red hair and multicolored costume hands out purple bead necklace to young child<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figcaption</span><span class="token punctuation">></span></span>Detroit Thanksgiving Day Parade 2022<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figcaption</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figure</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<p>Thanks to <a href="https://twitter.com/pauljadam/status/1285979673722322947">Paul Adams</a> for helping me learn the differences between alternative text, long desciption, and caption.</p>
<h2 id="additional-resources">Additional Resources</h2>
<ul>
<li>
<p><a href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML#text_alternatives">HTML Text Alternatives</a></p>
</li>
<li>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption"><code><figcaption></code>: The Figure Caption Element</a></p>
</li>
<li>
<p><a href="https://www.w3.org/WAI/tutorials/images/decision-tree/">An alt Decision Tree</a></p>
</li>
</ul>
A Theory of Web Relativity
2022-12-21T00:00:00Z
https://htmhell.dev/adventcalendar/2022/21/
<p><strong>The rel attribute has the potential to take the Internet to the next level⦠and yet, we usually forget about it.</strong></p>
<p>Imagine a city where people guided themselves by the landmarks and the stores, where there were no directional signs, and where streets and neighborhoods had no names. It may sound like a U2 song, but it is the current state of the web.</p>
<p>The Internet is a vast network of related pages and nodes, but the connections between the elements are void of meaning by default. They lack semantics. A link may connect pages A and B, but <em>what</em> is the relationship between the two? <em>Why</em> are they related? Wouldnāt it be great if there was a way of expressing the relationship between the Internet nodes? Then we wouldnāt have just data; we would have meaningful information. It would take the Internet to a whole new level.</p>
<p>Fortunately, it is possible. Thanks to the <code>rel</code> attribute.</p>
<h2 id="the-rel-attribute">The rel attribute</h2>
<p>The <code>rel</code> attribute defines a relationship between the linked resources (one being the document it appears on). And it provides that relationship with meaning, creating a semantic network where the different pieces have a role and a purpose. This detail may go unnoticed by most browser users, but not for the browser itself, assistive technologies, and web crawlers, which can take advantage of this attribute to provide a better experience. Also, savvy web developers can use it to make friendlier sitesāweāll see some examples in the following sections.</p>
<p>The <code>rel</code> attribute is valid in elements that link the document with another resource: <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link"><link></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a"><a></a>/<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area"><area></a>, and <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form"><form></a>. For simplicity purposes, when talking about <code><a></code>, it will include both <code><a></code> and <code><area></code>.</p>
<p>The supported values and how the browser treats them depend on the element in which the <code>rel</code> attribute appears. This is logical because the relationship can be a <em>how</em> or a <em>what</em>, and it not always makes sense to provide the ā<em>how</em>ā in some links.</p>
<p>Also, some <code>rel</code> values can be combined to generate more options and provide a more extended experience.</p>
<h2 id="link-types">Link Types</h2>
<div class="highlight">
<p>Authorās Note: in this article, I will refer to the values/types defined in the <a href="https://www.iana.org/assignments/link-relations/link-relations.xhtml">IANA link relationship registry</a> and the <a href="https://microformats.org/wiki/existing-rel-values">microformats wiki</a>, but the main focus will be on the <a href="https://html.spec.whatwg.org/multipage/links.html#linkTypes">values listed in the HTML Living Standard</a>.</p>
</div>
<p>I tried to group the types in categories depending on their function and the relationship they express. These are made-up categories to organize my work and they are not official in any way.</p>
<ul>
<li><a href="https://htmhell.dev/adventcalendar/2022/21/#meta-types">Meta types</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/21/#personal-types">Personal types</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/21/#processing-types">Processing types</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/21/#network-types">Network types</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/21/#sequential-types">Sequential types</a></li>
</ul>
<h2 id="meta-types">Meta types</h2>
<p>āMeta linksā provide information relevant to the document they appear in: meta information. Things like whoās the author? Where can we find an alternative version of the document? What is the license? Because of their nature, they can appear on <code><a></code> or <code><link></code> (although sometimes not on both) and never on a <code><form></code> tag.</p>
<h3 id="alternate">alternate</h3>
<p>The āalternateā relationship usually specifies an alternative version of the document itself, but some exceptions exist. It will be the most complex of all the <code>rel</code> meta values because the meaning it provides is heavily conditioned to the context in which it is presented.</p>
<p><strong>If it appears along with the</strong> <code>**stylesheet**</code> <strong>keyword</strong>, the linked file is an alternative stylesheet. It wonāt be applied to the document, but it will be ready for when we need it. For example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></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>main-styles.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet alternate<span class="token punctuation">"</span></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>dark.css<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dark mode<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<blockquote>When using `stylesheet` with `alternate`, the `<link />` must have a `title` attribute with a short (and non-empty) description.</blockquote>
<p>In this case, the ādark-styles.cssā file is loaded asynchronously and not applied to the document. This way, the styles will be available whenever we want to apply them, without having to wait for network or traffic. Some browsers, like Firefox, allow switching between styles, facilitating the process for users:</p>
<p>
<img src="https://htmhell.dev/images/advent2022/22/ff_styles.png" alt="Firefox lets you pick alternative styles from the View menu" loading="lazy" />
</p>
<p><strong>If it appears with a <code>type</code> attribute that has the value <code>application/rss+xml</code> or <code>application/atom+xml</code></strong>, then the <code>alternate</code> keyword creates a hyperlink referencing a syndication feed (in RSS or Atom respectively), which is useful for crawlers to find more content. In this case, it is not an exact alternative for the document, but it provides additional information about it, making it great for autodiscovery.</p>
<p>It can work both in <code><link></code> and <code><a></code>, and the meaning is the same in both:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<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>application/rss+xml<span class="token punctuation">"</span></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>feed.rss<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<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>application/atom+xml<span class="token punctuation">"</span></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>atom.xml<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>feed.rss<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<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>application/rss+xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Subscribe to my feed and never miss a blog!</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<p>This combination doesnāt impact our siteās visitors, but it provides us with control over how or what we want web crawlers to process our site. If there are multiple linked resources of this type, the first one is considered the main/default one, so the order would be important in that scenario.</p>
<p><strong>In any other case</strong>, the <code>alternate</code> keyword indicates that the linked element is an alternative version of the current document. But it doesnāt end there: the attributes that accompany the <code>rel</code> will provide additional information about the linked resource.</p>
<ul>
<li><code>media</code>: can be used to specify the target devices (mobile, print, etc.) for the alternative version.</li>
<li><code>type</code>: used to identify what format (PDF, XML, etc.) the linked alternative version is available on.</li>
<li><code>hreflang</code>: defines which language (English, Spanish, etc.) the linked resource is in.</li>
</ul>
<p>This opens a world of combinations and possibilities. Again, maybe not so much for the browser users, but definitely for bots and crawlers that need to process the information as accurately as possible.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token comment"><!-- alternative document versions in German, Spanish, and French --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</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>/de<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</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>/es<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>es<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</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>/fr<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fr<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- alternative document version in Spanish in PDF, specific for printing --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</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>/es/print/<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>es<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>application/pdf<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>print<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Thanks for checking out my online resume, feel free to download a</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>resume.pdf<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<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>application/pdf<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> PDF version.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://m.link-to-our-site.com<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>handheld<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Check out a mobile version of this site too!</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<p>Remember: the order of the attributes is not important, but for some cases, the order of appearance of the tag may define which is more <em>important</em> or the default one.</p>
<h3 id="author">author</h3>
<p>This may seem like an easy one: the āauthorā <code>rel</code> is a hyperlink that will point to the author (it could be <code>mailto:</code> with an email). But thereās a catch, the author of what? the page? the content inside? what?</p>
<p>The answer to these questions will depend on where the author link appears: if itās inside an <code><article></code>, then it will point to the author of the article, if itās outside of an <code><article></code> then it applies to the document/page as a whole.</p>
<p>Letās review it with an example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Article content --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Written by <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://janedoe.com<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>author<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Jane Doe<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- rest of the page --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Site created by <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://johndoe.com<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>author<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>John Doe<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>The first link appears inside an <code><article></code> which let us know that the link is pointing to the author of the article (Jane Doe). While the second link is outside any <code><article></code>, which tells us that is the author of the whole page (John Doe). <strong>This could be convenient in sites with syndicated content (like blogs or newspapers), to set a difference between the articleās author and the siteās owner/author</strong>.</p>
<p>Another way of specifying the pageās author is having an actual <code><link></code> tag in the pageās <code><head></code> with the <code>rel</code> āauthorā. Then we know for sure that it applies to the whole page and not just to a part of it:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>author<span class="token punctuation">"</span></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>https://alvaromontoro.com<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="bookmark">bookmark</h3>
<p>This <code>rel</code> value can only appear in <code><a></code>/<code><area></code>. It identifies a permalink for the nearest ancestor <code><article></code> or section (which doesnāt necessarily have to be a <code><section></code> element). This last part can be confusing, letās review it with an example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>James Bond Movies<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>no-time-to-die<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>No Time To Die<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> The twenty-fifth movie in the James Bond series. Daniel Craig was </span><br /><span class="highlight-line"> James Bond in this installment.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>no-time-to-die.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bookmark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Permalink<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>goldeneye<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Goldeneye<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> A movie and an amazing Nintendo 64 videogame. The iconic Pierce</span><br /><span class="highlight-line"> Brosnan played the role of James Bond on this movie.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>goldeneye.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bookmark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Permalink<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">...</span></code></pre>
<p>The first link with <code>rel= ābookmarkā</code> (permalink for No Time To Die) is inside of an <code><article></code>, so it will apply to that article in particular. The second link with <code>rel= ābookmarkā</code> (permalink for Goldeneye) is outside of an <code><article></code>, applying to the closest section, which will be defined by the earliest ancestor with a heading.</p>
<p>Notice how, if the bookmark link is not inside an <code><article></code>, it can be a bit tricky to identify what section it applies to. It is more important than ever to use the right markup.</p>
<h3 id="canonical">canonical</h3>
<p>A canonical link specifies the preferred version of the document. This is really helpful if we cross-post content online: while a search engine indexes the pages, it will be able to identify the <em>original</em> version and not penalize the author for having duplicated content.</p>
<p>Many blogging systems (e.g., Medium, DEV, Hashnode, etc.) allow for specifying a canonical URL for the articles, and placing a <code><link></code> element with <code>rel="canonical"</code> just like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>canonical<span class="token punctuation">"</span></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>https://path-to-preferred-url-version<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">...</span></code></pre>
<p>According to the HTML standard, it is only allowed on a <code><link></code> element, but the microformats definition mentions it on the <code><a></code> elements too. In my opinion, it would make sense to allow <code>rel="canonical"</code> in an <code><a></code> tag, but it could be problematic for publications. Better to stick to the HTML standard and only use canonical inside a <code><link></code> in the <code><head></code>.</p>
<h3 id="help">help</h3>
<p>This keyword can be used in all the available elements: <code><link></code>, <code><a></code>, or <code><form></code>. In all of them, it identifies a resource that contains help, the difference will be the scope of that help:</p>
<ul>
<li>In a <code><link></code>, it will link to a help resource related to the document as a whole.</li>
<li>In an <code><a></code>, it will be help related to the section that contains the anchor.</li>
<li>In a <code><form></code>, it specifies that the target contains information about the form and its contents.</li>
</ul>
<p>In the following example, the help link applies to the section that contains the label and input:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>phone<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Phone number<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>phone<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>phone<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>phone-help.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>help<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>(Help)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">...</span></code></pre>
<p><strong>Browsers and assistive technologies can use this link to provide a more standard computer behavior.</strong> For example: historically, pressing F1 opens the help functionality; So they could open the linked resource if the special key is pressed while on the section to which the help link applies.<br />
Also, in some cases, browsers may style the cursor differently if a link has the <code>rel="help"</code>. (And if they donāt, it may be a nice feature to add by developers.)</p>
<h3 id="icon">icon</h3>
<p>This <code>rel</code> value can only be used within a <code><link></code>, usually in the <code><head></code>, and it should link to an image. That image will then be displayed for representing the page in the user interface.</p>
<p>The <code>rel="icon"</code> can be combined with other attributes such as <code>type</code>, <code>media</code>, or <code>sizes</code> which will help the browser pick the most adequate icon for the user.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></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>fav.ico<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></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>favicon.png<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>image/png<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></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>small.ico<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>16x16 32x32 48x48<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></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>large.ico<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>128x128 512x512 1024x1024<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></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>iphone.png<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>57x57<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>image/png<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></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>bw.ico<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>screen and (max-color:2)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>You might have found a <code>rel</code> value of <code>shortcut icon</code> (in that order) instead of just <code>icon</code>, that was the old way of writing it and browsers still support it for historical reasons, but thereās no reason to do that anymore.</p>
<p>Another thing that we find in the wild is a <code>rel</code> value of <code>apple-touch-icon</code>. This is a specific value for Safari, only supported on Apple devices. Feel free to use it if you want to provide specific icons for them, but remember that it is not a standard value.</p>
<h3 id="license">license</h3>
<p>The <code>license</code> value can be used in all the supported tags (<code><link></code>, <code><a></code>, and <code><form></code>) to create a hyperlink to the copyright license terms under which the main content is provided. Notice the term āmain contentā instead of ācurrent documentā that is used throughout the article. This is because the license applies to the content itself, and the page/template may have a different copyright or license.</p>
<p>The specification does not specify how to differentiate the main content from the rest of the content. The link with <code>rel="license"</code> must be presented in a way that makes it clear to the user what content it applies to.</p>
<p>The following example simulates a website with images/videos and the license hyperlink for one of the images:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figure</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>image.jpg<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>Image for this demo<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figcaption</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> An demo image for the article. License:</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://creativecommons.org/licenses/by/4.0/<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>license<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> CC BY 4.0</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figcaption</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figure</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">...</span></code></pre>
<p>The value <code>copyright</code> was accepted in the past as a synonym of <code>license</code>, and browsers still support it, but we should go with the standard <code>license</code>.</p>
<h3 id="manifest">manifest</h3>
<p>The <code>manifest</code> relationship can only happen in a <code><link></code>. It indicates that the linked resource is a manifest file with metadata related to the current document.</p>
<p>The manifest may contain information about the entry file to install the web application, the icons to use, the appās name, theme colors⦠In this article, we wonāt dig into how to use the manifest as our focus is exclusively on the <code>rel</code> attribute used to link it.</p>
<p>Hereās how you can link your manifest from the <code><head></code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>manifest<span class="token punctuation">"</span></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>/manifest.json<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> ...</span></code></pre>
<p>s<br />
Two important things to remember about this <code>rel</code> value:</p>
<ol>
<li>Browsers will use the first <code><link></code> with <code>rel="manifest"</code> in the tree order, ignoring any other that may appear.</li>
<li>Browsers will execute the manifest when they consider it necessary, never assume that the entry point script will be executed.</li>
</ol>
<h3 id="pingback">pingback</h3>
<p>A pingback is a method for authors to get notifications when their content is linked. With <code>pingback</code>, we can provide the address of the pingback server that handles the pingbacks to the current document:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pingback<span class="token punctuation">"</span></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>https://alvaromontoro.com/pingback<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>This can be useful, for example, to process the notification and add a note on the page (like WordPress does). It can also be helpful for SEO.</p>
<p>The <code>pingback</code> value can only be used in the <code><link></code> element.</p>
<h3 id="search">search</h3>
<p>The <code>search</code> keyword indicates that the linked resource provides a way of searching the document and its related resources. It redirects to a search page.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Read more about this topic by <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/search<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>searching this</span><br /><span class="highlight-line"> site<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> or <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://google.com/search?q=rel+attribute<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> checking on Google<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>Notice how it doesnāt have to be an internal search functionality. In the example above, one of the search links points to Google.</p>
<p>Some browsers allow adding plugins to their interfaces to provide custom searching features. If we have an OpenSearch plugin, we can link it using <code>type="application/opensearchdescription+xml"</code> alongside the <code>search</code> value to add the plugin easily to the browser.</p>
<h3 id="tag">tag</h3>
<p>The linked resource contains information about a tag or keyword related to the current document. It can be a description, a list of other elements from the same category, etc. This <code>rel</code> value is only valid on <code>a</code> and <code>area</code> elements.</p>
<p>A practical use of the <code>tag</code> value could be seen on blogs or other sites where content can be categorized. <a href="https://stackoverflow.com/">StackOverflow</a> is a great place to see links with <code>rel="tag"</code> working live. Or we could have something like this on our blog:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>The Definitive Guide of Colors in CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Tags<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/tags?t=css<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tag<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/tags?t=htm<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tag<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>HTML<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/tags?t=webdev<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tag<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Web Development<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">...</span></code></pre>
<p>The <code>rel="tag"</code> links apply to the whole page independently of where they appear within it. Even if they are located inside an article, they are not exclusive to the article itself.</p>
<p>
<img src="https://htmhell.dev/images/advent2022/22/tag.png" alt="Screenshot of StackOveflowās use of rel=tag" loading="lazy" width="740" height="137" />
</p>
<h2 id="personal-types">Personal Types</h2>
<p>They indicate a relationship between the author of the document/article and the linked resource (which should be an online profile). The HTML standard only has one of this type (āauthorā), but the XHTML Friends Network (XFN) defines many more:</p>
<ul>
<li><code>author</code>: see description in the meta types.</li>
<li><code>crush</code>: the linked page represents a person you have a crush on.</li>
<li><code>date</code>: the hyperlink is to a page about someone you are dating.</li>
<li><code>friend</code>: the link identifies a friend.</li>
<li><code>me</code>: <strong>this value became really popular with the rise of Mastodon</strong>. It is used to link to a page that represents the same person as the current document.</li>
<li><code>muse</code>: a link that identifies a person who inspires the person.</li>
</ul>
<p>As you may imagine, there are <a href="https://gmpg.org/xfn/11">many more values that could be used</a>. Quite often, just using the English word for the personal relationship will work as a valid <code>rel</code>: colleague, child, acquaintance, neighbor, parent⦠all good.</p>
<p>Here are some of these personal <code>rel</code> values in action:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Welcome!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> My name is <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>profile.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>me<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Ross Geller<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>, thanks for </span><br /><span class="highlight-line"> visiting my personal website!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> I live in NYC with my buddies <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>j-man.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>friend<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Joey<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> and <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>king-of-bad-thanksgivings.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>friend<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Chandler<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>. </span><br /><span class="highlight-line"> In the same building block as famous cook (and my younger sister)</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>monica.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sibling<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Monica Geller<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Monica lives with <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>rachel.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>crush date<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Rachel<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> and </span><br /><span class="highlight-line"> renown musician <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>regina-phalange<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>muse friend<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Phoebe<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> (author of the smash hit "Smelly Cat").</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>One last point about this personal type: only the āauthorā value is supported in a <code><link></code> tag, all the others are only allowed on <code><a></code>.</p>
<h2 id="processing-types">Processing Types</h2>
<p>These types donāt indicate what the linked resource is or how it is related to the current document, instead, <strong>they tell the browser how the link should be treated or processed</strong>, creating a connection that may not be completely semantic but making it special.</p>
<p>All of these values are <strong>only accepted in the <code><link></code> element</strong>, they will normally appear in the <code><head></code>, and will mainly impact performance.</p>
<h3 id="dns-prefetch">dns-prefetch</h3>
<p>The <code>dns-prefetch</code> keyword indicates that the browser should preemptively perform DNS resolution for the specified domain. Basically, we are telling the browser that we will likely need some resources from the linked domain, so it will be beneficial and good for the user experience if it started resolving the address and getting ready to ask for files.</p>
<p>Using this type of <code>rel</code> reduces the latency when requesting an online resource and improves the overall performance, giving the impression that the content loaded faster (because some of the load work was done before it was actually needed.)</p>
<p>For example, if we are using Google Fonts, and we know that our page will at some point need the font, we can prefetch the CDN where Google hosts the fonts:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dns-prefetch<span class="token punctuation">"</span></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>https://fonts.googleapis.com<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>If you have used Google Fonts before, you might have noticed that it doesnāt use <code>rel="dns-prefetch"</code> but <code>rel="preconnect"</code> (we will analyze it soon). This is because <code>dns-prefetch</code> only performs DNS resolution, while <code>preconnect</code> does DNS resolution and also establishes a connection to the server, making the process <em>faster</em>.</p>
<p>Which raises a fair question: why even use <code>dns-prefetch</code> if <code>preconnect</code> does more? Because network connections are āexpensiveā. If we need sources from many third-party domains, preconnecting to all of them may impact performance negatively. Instead, it would be better to preconnect only to the more āurgentā ones and do a <code>dns-prefetch</code> for the rest. Thereās no risk in using both together (even for the same domains!) and if for some reason the browser doesnāt support them, it wonāt impact our site at all (apart from having to load a couple more lines of code.)</p>
<h3 id="modulepreload">modulepreload</h3>
<p>JavaScript code is getting bigger and bigger, and it is becoming more important to split the code and organize it into simpler modules that can be run when needed. NodeJS and some frameworks have their own methods for module usage, and some browsers provide native module functionality too. This is where the <code>modulepreload</code> value is important.</p>
<p>We can use <code>rel="modulepreload"</code> to preload a module script and place it in the appropriate module map without evaluating it (that will happen when it is required). This optimizes the module load, making the process more efficient.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modulepreload<span class="token punctuation">"</span></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>app.mjs<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modulepreload<span class="token punctuation">"</span></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>record-player.mjs<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modulepreload<span class="token punctuation">"</span></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>trade.mjs<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modulepreload<span class="token punctuation">"</span></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>payment.mjs<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>module<span class="token punctuation">"</span></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>app.mjs<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span></code></pre>
<p>In the example above, we are preloading several scripts. The main module is executed (<code>app.mjs</code>), and the others will be available whenever they are needed (<code>record-player.mjs</code>, <code>trade.mjs</code>, <code>payment.mjs</code>).</p>
<p><code>modulepreload</code> is basically a specialized version of the <code>preload</code> keyword (see below), as it applies specifically to one type of resource: script modules.</p>
<h3 id="preconnect">preconnect</h3>
<p>As mentioned in the <code>dns-prefetch</code> section, with the <code>preconnect</code> keyword, we indicate to the browser that a resource from the linked domain will most likely be needed, and initiate a connection to its origin before the resource has even been found by the browser.</p>
<p>By doing so, we are increasing the network performance, because we are establishing a connection that will be ready when itās needed, reducing the latency, and improving the user experience.</p>
<p>A practical example of <code>preconnect</code> can be found on Google Fonts: you may have noticed the code to embed fonts includes a couple of extra lines:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preconnect<span class="token punctuation">"</span></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>https://fonts.googleapis.com<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preconnect<span class="token punctuation">"</span></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>https://fonts.gstatic.com<span class="token punctuation">"</span></span> <span class="token attr-name">crossorigin</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</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>https://fonts.googleapis.com/css2?family=Roboto&display=swap<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>The first two <code><link></code> use <code>preconnect</code> while the third <code><link></code> imports a stylesheet which will bring the font. Those first two lines initiate a connection for loading content from the Google CDNs, so the browser is ready to download the files.</p>
<h3 id="prefetch">prefetch</h3>
<p>With <code>prefetch</code>, we identify a resource that may be used by the subsequent pages, telling the browser that it should fetch that resource to deliver a faster response when the navigation to those pages actually happens. The browser will proceed to load the content into the cache for later use.<br />
Prefetching some files for later useāan image, some script, and a song:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>prefetch<span class="token punctuation">"</span></span> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image<span class="token punctuation">"</span></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>super-important-image.jpg<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>prefetch<span class="token punctuation">"</span></span> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>script<span class="token punctuation">"</span></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>code.js<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>prefetch<span class="token punctuation">"</span></span> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio<span class="token punctuation">"</span></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>https://somecdn.com/music.mp3<span class="token punctuation">"</span></span> <span class="token attr-name">crossorigin</span> <span class="token punctuation">/></span></span></span></code></pre>
<p>The <code>as</code> attribute is optional, but highly recommended (see <code>preload</code> section for more details.)</p>
<p>Use <code>prefetch</code> carefully. It is a little bit of a āgambleā: we are fetching content that we presume the user will need soon, but if the user doesnāt navigate the way we expect it, we might have used bandwidth unnecessarily.</p>
<h3 id="preload">preload</h3>
<p>With <code>preload</code>, we ask the browser to fetch some resources and process them as specified with the <code>as</code> attribute, because those files are important and we will need them on the current document.<br />
In this example we will preload an image, a script file, and an audio file:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preload<span class="token punctuation">"</span></span> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image<span class="token punctuation">"</span></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>super-important-image.jpg<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preload<span class="token punctuation">"</span></span> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>script<span class="token punctuation">"</span></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>code.js<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preload<span class="token punctuation">"</span></span> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>audio<span class="token punctuation">"</span></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>https://somecdn.com/music.mp3<span class="token punctuation">"</span></span> <span class="token attr-name">crossorigin</span> <span class="token punctuation">/></span></span></span></code></pre>
<p>Notice the <code>as</code> attribute because it is not trivial. It tells the browser what type of resource is being linked so it can process it adequately. Without it, the browser may end up fetching the content twice (the same thing with the <code>crossorigin</code> attribute.)</p>
<p>So, how are <code>preload</code> and <code>prefetch</code> different? While they seem to do the same, thereās a key difference: <code>prefetch</code> <strong>is used for ālower priorityā resources that will be needed at a later time,</strong> and <code>preload</code> <strong>is for āhigher priorityā content that will be needed sooner</strong> (as on the same page, but that may have not been discovered by the browser yet.)</p>
<p>I will end this section by giving a piece of advice: <strong>do not preload everything</strong>. Quoting The Incredibleās supervillain Syndrome: ā<em>When everyone is super, no one will be</em>.ā If we preload every file that we will need, then there wonāt be a real gain (and it may even be counterproductive). Be selective with the content to preload, and consider using <code>prefetch</code> or <code>preconnect</code>.</p>
<h3 id="prerender">prerender</h3>
<p><code>prerender</code> works similarly to <code>prefetch</code> but at a larger scale. The linked resource may be a page, and it is not only prefetched but also processed (including its content) and rendered, caching the result until itās needed.</p>
<p>Imagine that you created a multi-page form. You may want to prerender the next step because the user should logically navigate to it:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>prerender<span class="token punctuation">"</span></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>form-step-2.html<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>As with <code>prefetch</code>, <code>prerender</code> could be a bit of a āgambleā (even more this time): if the user doesnāt navigate the site the way we expect or if they leave the site completely, we will have fetched and processed content unnecessarily.</p>
<h3 id="stylesheet">stylesheet</h3>
<p>Surprised to see this value as part of the processing types? I considered adding it to the meta types, but it fits here better.</p>
<p>The <code>stylesheet</code> indicates that the linked resource contains styles for the page, so it provides a little semantics; but it does much more: browsers know that stylesheets are a key part of the pageās presentation and, unless the <code>alternate</code> keyword shows up too, it also knows that it needs to process the styling file and apply it to the page. The <code>stylesheet</code> keyword is telling the browser how to treat the linked file.</p>
<p>Its basic use is simple: only in <code><link></code>, the <code>rel="stylesheet"</code>, and an <code>href</code> pointing to the stylesheet file. We can also use other attributes (<code>media</code>) to change the default behavior and when the styles will be applied:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- this file will be automatically downloaded and applied --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></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>my-super-cool-styles.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- this one is downloaded but not applied --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet alternate<span class="token punctuation">"</span></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>prototype.css<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>prototype<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- this one is downloaded but only applied when printing --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></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>printer.css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>print<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<h2 id="network-types">Network Types</h2>
<p>The ānetwork typesā specify the type of connection that the document and the linked resource have as well as the information that will be shared between them.</p>
<p>They donāt create a link, they annotate it, making it easier for crawlers and spiders to index the content appropriately. As they will have more information about the nature of the connection.</p>
<h3 id="external">external</h3>
<p>A resource is external if it is not part of the current site. We can easily identify them by adding a <code>rel="external"</code> to it, which is allowed on <code><a></code>/<code><area></code> or the target in a <code><form></code>, but never on a <code><link></code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- on a personal blog --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://en.wikipedia.org/wiki/Cat<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Read about cats<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>This doesnāt give much information to the browser (it is transparent for it), but it can be convenient for developers to use CSS attribute selectors and style these links differently:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* add a small icon behind each external link */</span></span><br /><span class="highlight-line"><span class="token selector">a[rel="external"]::after</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>external-icon.svg<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin-left</span><span class="token punctuation">:</span> 0.5rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">vertical-align</span><span class="token punctuation">:</span> middle<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="nofollow">nofollow</h3>
<p>A <code>nofollow</code> rel value on a link tells the crawlers to āignoreā the linked resource because it is not endorsed by the current documentās owner, or that it was included because of a commercial relationship.</p>
<p>It is an important value for Search Engine Optimization (SEO): if your page links to low-quality content, your siteās credibility (and its ranking in the search engines) may suffer. Using <code>nofollow</code> wonāt penalize your site:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://totallynotmischivieoussite.com<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>nofollow<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> This is a link created by an user</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>Using <code>rel="nofollow"</code> in user-generated content and links is a must. You never know what they are going to share, and you donāt want to end up on the losing side of a spamming scheme.</p>
<h3 id="noopener">noopener</h3>
<p>A link may have access to the previous page by using <code>window.opener</code> depending on how it was opened (e.g. if it has <code>target="_blank"</code>). This is a potential security problem and it could lead to other issues like phishing attempts.</p>
<p>To avoid these situations, we can use <code>noopener</code> value in <code>rel</code> to open the new document without granting it access to the document that opened it:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>comment<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Check my <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>noopener<span class="token punctuation">"</span></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>https://no-good-phishy-site.tk<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>site<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">...</span></code></pre>
<p>As with <code>nofollow</code>, the main target for this value will be user-generated content that we donāt want to provide with extra information.</p>
<h3 id="noreferrer">noreferrer</h3>
<p><code>noreferrer</code> is really close to <code>noopener</code>. In fact, it does the same thing as <code>noopener</code> (it doesnāt grant access to the document) plus it tells the browser to omit the <code>Referer</code> header when opening the link. This way, the linked resource cannot access the referrerās information (e.g., its address).</p>
<p>This provides an extra layer of anonymity and security. The linked resource will not see a referrer, looking like the request came from direct traffic to their site instead of from the originating link.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">...</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>comment<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Check my <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>noreferrer<span class="token punctuation">"</span></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>https://no-good-phishy-site.tk<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>site<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">...</span></code></pre>
<p><code>noreferrer</code> and <code>nofollow</code> are often used together, to ensure that one is applied in case the other one is not supported by the browser.</p>
<h3 id="opener">opener</h3>
<p>This is effectively the opposite of <code>noopener</code>. It doesnāt add that extra layer of protection when following the link by allowing it to access the opener.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>./help-page.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>opener<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>You have access<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="sequential-types">Sequential Types</h2>
<p>These values indicate that the current document is part of a series or sequence and specifies the relationship of the linked resource with it. They have a big impact on user experience, accessibility, and SEO.</p>
<p>The HTML Living Standard only has two values related to sequences: ānextā and āprevā, but there are some interesting ones defined in previous versions of the standard that are listed by IANA and microdata too:</p>
<ul>
<li><code>next</code>: indicates that the linked resource is the next in a series.</li>
<li><code>prev</code>: refers to the linked document as the previous in a series.</li>
<li><code>previous</code>: this value is a synonym of āprevā. It isnāt valid anymore, so we should avoid it, but <a href="https://html.spec.whatwg.org/multipage/links.html#link-type-prev">browsers may still treat it as a āprevā for historical reasons</a>.</li>
<li><code>first</code>: identifies the linked document as the first in a series.</li>
<li><code>last</code>: indicates that the linked resource is the last in a series.</li>
<li><code>contents</code>: the linked document is a table of contents. Some agents may use the synonym <code>toc</code> too.</li>
<li><code>archives</code>: links to a collection of records or materials of historical interest for the document.</li>
<li><code>up</code>: refers to the parent/upper category of the current document. Not necessarily in a sequence, but it shows some hierarchy.</li>
</ul>
<p>Hereās a practical example of how it could be used to create pagination:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>course index<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>toc.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>contents<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Table of Contents<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lesson-1.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>first<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lesson 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lesson-2.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lesson 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lesson-3.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>up<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lesson 3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lesson-3.1.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>prev<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lesson 3.1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lesson-3.2.html<span class="token punctuation">"</span></span> <span class="token attr-name">aria-current</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lesson 3.2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lesson-3.3.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>next<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lesson 3.3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lesson-4.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lesson 4<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lesson-5.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lesson 5<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lesson-6.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>last<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lesson 6<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>glossary.html<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>glossary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Glossary<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<p>In this example, we have a navigation with eleven links to the table of contents, each of the six lessons (plus three āsub lessonsā), and a glossary page. Many of those links have been extended using the <code>rel</code> attribute and provide helpful information to the browser and assistive technologies.</p>
<p><strong>One perk of using ānextā and āprevā with a <code><link></code> is that the browser may preload the linked documents</strong> to reduce the perceived load time, assuming that the user will most likely move forward/backward within the series.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>next<span class="token punctuation">"</span></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>https://link-to-next-in-series<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>prev<span class="token punctuation">"</span></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>https://link-to-previous-in-series<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>In the past, Google used the ānextā and āprevā links to index the pages accordingly, but <a href="https://twitter.com/JohnMu/status/1108717486424363009">that is no longer the case</a>. This doesnāt mean you should stop using those values: they are still helpful for accessibility, and other search engines may index based on them. Google is dominant but not the only one.</p>
<h1>Conclusion</h1>
<p>As you may have noticed by this really long article, the <code>rel</code> attribute is anything but trivial. It plays an important role in providing meaning to the links, taking the Internet to the next level semantically, and boosting your siteās performance through the roof.</p>
<p>If you made it here (did I already mention that this article is really long?), take a few more minutes to check other resources with more details and better explanations (Addy Osmaniās article is simply amazing.)</p>
<p>Thanks for reading!</p>
<h1>Bibliography</h1>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a"><code><a></code>: The Anchor element</a>, on MDN Web Docs.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area"><code><area></code>: The Image Map Area element</a>, on MDN Web Docs.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form"><code><form></code>: The Form element</a>, on MDN Web Docs.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link"><code><link></code>: The External Resource Link element</a>, on MDN Web Docs.</li>
<li><a href="https://microformats.org/wiki/existing-rel-values">Existing rel values</a>, on the microformats wiki.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel">HTML Attribute: rel</a>, on MDN Web Docs.</li>
<li><a href="https://www.iana.org/assignments/link-relations/link-relations.xhtml">Link Relations</a>, on the Internet Assigned Numbers Authority (IANA).</li>
<li><a href="https://html.spec.whatwg.org/multipage/links.html">Links</a>, on the HTML Living Standard.</li>
<li><a href="https://www.hixie.ch/specs/pingback/pingback">Pingback 1.0</a>, by Ian Hickson.</li>
<li><a href="https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf">Preload, Prefetch And Priorities in Chrome</a>, by Addy Osmani on Medium.</li>
<li><a href="https://nitropack.io/blog/post/resource-hints-performance-optimization">Preload, Preconnect, Prefetch: Improve Your Siteās Performance with Resource Hints</a>, by Niko Kaleev on Nitropack.</li>
<li><a href="https://www.w3.org/TR/resource-hints/">Resource Hints (Working Draft)</a>, by Ilya Grigorik on the W3C.</li>
<li><a href="https://gmpg.org/xfn/11">XFN 1.1 relationships meta data profile</a>, on the GMPG.</li>
</ul>
Common nesting issues in HTML
2022-12-20T00:00:00Z
https://htmhell.dev/adventcalendar/2022/20/
<p>HTML is such a lovely language. Browsers will almost always display something for you, no matter what you put in the HTML document. Heck, you could omit all tags, and it will still work.</p>
<p>Thatās all nice, but as web professionals, we should aim to write valid code. But even professional web developers make mistakes. Here are some examples of common nesting issues in HTML.</p>
<h2 id="anchors-and-buttons">Anchors and buttons</h2>
<p>Letās start with anchors and buttons. It is common to see buttons inside anchors out in the web wilderness.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Button<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>According to the specification, the anchor element must not contain any other interactive element, so <code><button></code> elements cannot live inside the <code><a></code> element.</p>
<blockquote><strong>Permitted content</strong><br />
Transparent, except that no descendant may be interactive content or an a element, and no descendant may have a specified tabindex attribute.</blockquote>
<p>- <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#properties">the a element on MDN</a></p>
<p>If we validate the code against the <a href="https://validator.w3.org/nu/">HTML validator</a>, we can see that it's invalid.</p>
<img src="https://htmhell.dev/images/advent2022/17/validator.png" alt="Error message in the HTML validator: The element button must not appear as a descedant of the a element." />
<p>In the example below, when you click on a button, you will get two alerts, which is far from ideal.</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="NWzXeEO" data-user="CiTA" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/CiTA/pen/NWzXeEO">
Nesting issue: <a> and <button></a> by Silvestar BistroviÄ (<a href="https://codepen.io/CiTA">@CiTA</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>You should avoid nesting interactive elements inside each other. It might affect user expectations and the functionality of your website negatively. If you need a link that looks like a button, <a href="https://www.htmhell.dev/4-link-also-button/">use the <code>a</code> element and style it like a button</a>. This also applies to links nested in buttons.</p>
<h2 id="lists">Lists</h2>
<p>Let's have a look at other common HTML elements, <code><ul></code> and <code><ol></code>. Their direct children shouldnāt be any other element than <code><li></code>, <code><script></code>, or <code><template></code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>List item 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>Div item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>List item 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Div item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>Div item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>Using <code>div</code> or similar elements as direct children of lists is wrong. The final output is messed up, and users might have issues distinguishing list items from non-list items, as seen in the example below. <code><span></code> items are tough to identify, as they are rendered inline with list items.</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="XWYVovm" data-user="CiTA" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/CiTA/pen/XWYVovm">
Nesting issue: <list></a> by Silvestar BistroviÄ (<a href="https://codepen.io/CiTA">@CiTA</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<p>Instead, only use allowed elements inside list elements.</p>
<blockquote><strong>Permitted content</strong><br />
Zero or more <code><li></code>, <code><script></code> and <code><template></code> elements.
</blockquote>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul">the ul element on MDN</a></p>
<p>Note that <code><dl></code>, the description list element, accepts <code><div></code> elements.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dl</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>HTML<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>HTML is aā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span>CSS is aā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dl</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="same-element-type-nesting">Same element type nesting</h2>
<p>Some HTML elements mustnāt be nested in their element type. For example, the <code><main></code> element shouldnāt be nested inside another <code><main></code> element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span></code></pre>
<p>According to the spec, there should only be one visible <code><main></code> element. That makes sense because there are no multiple main contents in a page. We want a single main element that holds the main content.<br />
If you donāt respect this rule, nothing will be broken visually, but this could affect SEO and the accessibility of a page.</p>
<blockquote>A document mustn't have more than one <code><main></code> element that doesn't have the <code>hidden</code> attribute specified.</blockquote>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main">the main element on MDN</a></p>
<p>The <code><header></code> and <code><footer></code> elements also shouldn't be nested inside themselves.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span></code></pre>
<p>The difference between these two and the <code><main></code> element is that you can have multiple <code><header></code> and <code><footer></code> elements in a document, if youāre using them inside <code><main></code>, <code><section></code>, <code><article></code>, <code><aside></code>, or <code><nav></code> elements.</p>
<p>When used outside of the these elements, they are considered as <a href="https://htmhell.dev/tips/landmarks/">landmarks</a>. In short, landmarks are organized areas of your page which help keyboard and screen reader users understand and navigate the page more easily.</p>
<p>Many other elements, like anchors and form elements, donāt allow nesting inside themselves either. However, unlike <code>header</code>, <code>footer</code>, and <code>main</code>, nesting these elements could break their functionality and styling.</p>
<h2 id="fieldset/legend">Fieldset/Legend</h2>
<p>The <code><legend></code> element is used to label its parent <code><fieldset></code>. It should be the first child of the <code><fieldset></code> element and it must not be nested inside another element.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>Legend<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>l<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>shirt1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>l<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Large<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>m<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>shirt1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>m<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Medium<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span></span></code></pre>
<p>The above code is wrong because since the <code>legend</code> is wrapped in a <code>div</code>, the <code>fieldset</code> has no accessible name. This may cause a group of radio buttons not being announced as a group and the <code>legend</code> might not have a sematic relation with the radio buttons. The visual appearance breaks as well. The legend isnāt positioned on the border of the fieldset, but below it.</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="PoagvKJ" data-user="CiTA" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/CiTA/pen/PoagvKJ">
Nesting issue: <legend></a> by Silvestar BistroviÄ (<a href="https://codepen.io/CiTA">@CiTA</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<blockquote><strong>Permitted parents</strong><br />
A <code><fieldset></code> whose first child is this <code><legend></code> element.</blockquote>
<p>- <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend">the legend element on MDN</a></p>
<p>Even if the <code>legend</code> is not the first child of the <code>fieldset</code>, visually it will be rendered as the first element within its parent. This can be confusing for screen reader users because the visual order doesn't match DOM order.</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="GRGdqqY" data-user="CiTA" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/CiTA/pen/GRGdqqY">
Nesting issue: <legend></a> by Silvestar BistroviÄ (<a href="https://codepen.io/CiTA">@CiTA</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<h2 id="conclusion">Conclusion</h2>
<p>Letās face it: an invalid HTML structure will not break your site in most cases, at least visually. Some might say thatās the beauty of HTML, but others say it is a curse.<br />
Nevertheless, a weak HTML structure might make your site less usable and affect your SEO score. Not only that, assistive technologies might not announce your content correctly. And finally, your code might be rendered incorrectly if browsers decide to penalise invalid HTML in the future (less likely, though). So try to avoid HTML errors at all costs.</p>
<p>There are all sorts of tools that you could use to help you avoid common issues, from online validator tools like <a href="https://validator.w3.org/nu/">Nu HTML Validator</a> to text editor extensions like <a href="https://marketplace.visualstudio.com/items?itemName=mkaufman.HTMLHint">HTMLHint</a>. If you're unsure about nesting one tag inside another, you could always go to <a href="https://caninclude.glitch.me/">Caninclude</a> for confirmation.</p>
Do you know color-scheme?
2022-12-19T00:00:00Z
https://htmhell.dev/adventcalendar/2022/19/
<p>Do you know of <code>color-scheme</code> yet? If not, I bet you still think you do. It will certainly look familiar, as <code>prefers-color-scheme</code> has been around for longer and is clearly related.</p>
<p>You're in good company if it's new to you - the <a href="https://2022.stateofcss.com/en-US/features/accessibility/#color_scheme">State of CSS 2022</a> results just came in, and 73% of respondents had never heard of it.</p>
<p>You probably use <code>prefers-color-scheme</code> a lot within your CSS media queries, to make a dark or light theme for your sites and apps based on user preference. Maybe you also add a toggle so people can choose their color scheme, irrespective of their OS settings. Beautiful. But now there's more to play with!</p>
<h3 id="what-is-color-scheme-then?">What is color-scheme, then?</h3>
<p>It's a CSS property all of its own, and can save you a bunch of styling work for dark mode.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* the selector here could be :root, html or body */</span></span><br /><span class="highlight-line"><span class="token selector">:root</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color-scheme</span><span class="token punctuation">:</span> light dark<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Stick the above inside your stylesheet or <code><style></style></code> tags and voila, your website, without any other styling, will now respond to the user's <code>prefer-color-scheme</code> preference, as can now often be specified in their OS settings. There are default colors set in the browser for bare HTML not only in light mode, but also in dark mode. Cool, right?</p>
<figure style="margin-bottom: 2.4rem">
<img src="https://htmhell.dev/images/advent2022/19/bare-html.png" alt="Screenshot of two bare-HTML mini-sites, one light, one dark" loading="lazy" />
<figcaption>Comparison of two mock plain HTML sites, one with color-scheme set to light, one set to dark.</figcaption>
</figure>
<p>If you're like me, and tend to create little simpler sites - or if you make bigger ones with minimal styling, and you want to provide dark mode with as little extra faff as possible, you will want to explore using <code>color-scheme</code>. There's another bit of magic here - you don't even need to use it with a <code><style></code> tag, or <code>.css</code> file at all, if you don't want. You can get dark mode with HTML only.</p>
<p>Yep, just HTML. Without even touching your CSS. You can also place it inside the <code><head></head></code> as a meta tag:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>color-scheme<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>light dark<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span></code></pre>
<p>The order of modes <code>light dark</code> or <code>dark light</code> can make a difference - a user with no preference set will see the first mode. However, given that already most current OS (mobile and computer) versions ask you if you want light or dark mode, or even a mode that changes from one to the other automatically over the day, this may soon become less effective.</p>
<h3 id="why-do-i-want-to-use-it?">Why do I want to use it?</h3>
<p>Many websites leave a bunch of defaults as they are - usually light mode defaults. If they just want a white background then why declare that again? You can now do the same for dark mode.</p>
<p><a href="https://dev.to/bcalou/why-you-should-always-set-a-background-color-2gb1">Some will suggest</a> that these colors <em>should</em> always be declared - but it really depends how minimal you want to be! Leave both the text and background as default, and they will both be controlled by the users themselves, via <code>color-scheme</code>.</p>
<p>You can see when other non-dark-mode sites have stuck with browser default colors, by opening up dev tools on them, and pasting that meta tag with just <code>dark</code> into their HTML. The bits that flip in color from light to dark (and where text goes from dark to light) were using the defaults.</p>
<p>Here's an example of this happening on the BBC News website, after I added a meta tag to make the <code>color-scheme</code> dark:</p>
<img src="https://htmhell.dev/images/advent2022/19/bbc-news.png" alt="BBC News Website with color-scheme: dark injected as a meta tag" loading="lazy" />
<p>Here you see they kept the main background default, but must have declared the text color - as the background darkens and renders the text almost invisible. In the "Most watched" and "Most read" sections, they've left both background and text default, and so they both flip.</p>
<h3 id="how-else-can-i-use-it?">How else can I use it?</h3>
<p>Of course you can use JavaScript to manipulate the content of the meta tag.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>color-scheme<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>light dark<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">lightMode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> document</span><br /><span class="highlight-line"> <span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'meta[name="color-scheme"]'</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"content"</span><span class="token punctuation">,</span> <span class="token string">"light"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">darkMode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> document</span><br /><span class="highlight-line"> <span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'meta[name="color-scheme"]'</span><span class="token punctuation">)</span></span><br /><span class="highlight-line"> <span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"content"</span><span class="token punctuation">,</span> <span class="token string">"dark"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Or just keep the property within a <code>*.css</code> file as with other CSS properties. The world's your oyster.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">:root</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color-scheme</span><span class="token punctuation">:</span> light dark<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>It's really worth experimenting. You can add the CSS to <code>:root</code>, <code>html</code>, <code>body</code>, or indeed any other element, as maybe you only want parts of your site to do the flip (you might choose to keep the header and footer the same in both modes, as an example).</p>
<h3 id="system-colors">System colors</h3>
<p>When targeting elements you will also need to declare at least the <code>background-color</code>, as most elements are by default transparent. You can do this with the system colors <code>Canvas</code> for the background and <code>CanvasText</code> for the default text color, as these both follow <code>color-scheme</code>.</p>
<p>There are more system colors! See them at the W3C: <a href="https://www.w3.org/TR/css-color-4/#css-system-colors">System Colors</a>.</p>
<p>A few of them are a bit variable across browsers - notably <code>LinkText</code> in some browsers doesn't always have enough contrast with the dark background in dark mode. <code>Canvas</code> and <code>CanvasText</code> are however more reliable and very useful!</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">section</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> Canvas<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> CanvasText<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">section.light</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color-scheme</span><span class="token punctuation">:</span> light<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">section.dark</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="ive-already-styled-everything-for-both-modes">I've already styled everything for both modes.</h3>
<p>Really, everything?</p>
<p>Know what else is great? <code>color-scheme</code> takes the default UI elements with it. The scroll bar, the select dropdowns, radio buttons and checkboxes - they all flip with <code>color-scheme</code> too, saving you all those little niggly UI styling jobs. Maybe you have already done this for your current project, but what about your next one?</p>
<img src="https://htmhell.dev/images/advent2022/19/codepen.png" alt="A minimally-styled form in both dark and light modes" loading="lazy" />
<p>Please feel free to fork and play with the <a href="https://codepen.io/sarajw/pen/xxzyOMZ">Codepen</a>.</p>
<h3 id="any-other-benefits?">Any other benefits?</h3>
<p>If you're happy to forgo a mode toggle, and want to also save some CSS loading time? Put your dark mode and light mode styles into separate <code>*.css</code> files, and then use the prefers-color-scheme media queries in the <code><link></code> tags in your head to only load the relevant styles.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></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>layout.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-color-scheme:light)<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></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>light.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-color-scheme:dark)<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></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>dark.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>Once you start allowing scheme toggling outside of the user OS-set preference, trying to do this does of course get more complicated - I would only urge you to experiment!</p>
<p>Have fun with it! You can thank me later ;)</p>
<h3 id="further-reading">Further reading:</h3>
<p>For some experimentation I did, feel free to check out <a href="https://color-scheme-light-dark.netlify.app/">https://color-scheme-light-dark.netlify.app/</a></p>
<p>Do check the dates on these! <code>color-scheme</code> is still relatively new, but it's much better supported than when a couple of these articles were written - but they remain good articles on the subject:</p>
<ul>
<li>Find out more about <code>color-scheme</code> at <a href="https://web.dev/color-scheme/">https://web.dev/color-scheme/</a> by <a href="https://toot.cafe/@tomayac">Thomas Steiner</a></li>
<li><a href="https://www.jim-nielsen.com/">Jim Nielsen</a> wrote about both <code>color-scheme</code> and system colors: <a href="https://blog.jim-nielsen.com/2021/css-system-colors/">https://blog.jim-nielsen.com/2021/css-system-colors/</a></li>
<li>More on system colors from <a href="https://front-end.social/@stefan">Stefan Judis</a>: <a href="https://www.stefanjudis.com/today-i-learned/css-defines-color-values-that-follow-system-preferences/">https://www.stefanjudis.com/today-i-learned/css-defines-color-values-that-follow-system-preferences/</a></li>
</ul>
<p>Please <a href="https://front-end.social/@sarajw">ping me a message on Mastodon</a> with any solutions you come up with that use <code>color-scheme</code> - I would love to see them!</p>
Mini-guide to add an image
2022-12-18T00:00:00Z
https://htmhell.dev/adventcalendar/2022/18/
<p>Adding an image with HTML is pretty easy, itās just a simple tag, after all, right?</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/image.jpg<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>But when you start taking into consideration topics such as performance, screen sizes, accessibility, pixel density, or user preferences, you might ask yourself at some point if plain HTML is enough for the task⦠And the answer is yes! HTML has many options and is powerful enough to handle this task. This article will cover what you should consider at the moment of adding an image to a site with HTML.</p>
<h2 id="basic-considerations">Basic considerations</h2>
<h3 id="alt-attribute"><code>alt</code> attribute</h3>
<p>Letās start with a very important one: your image needs an alternative text. Itāll have screen reader users to identify the image content, but itāll also help when the image doesnāt load (maybe because the image is not available, or maybe because the user has a slow network). Otherwise, they wonāt know what the image is. This can be done with the <code>alt</code> attribute.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/image.jpg<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>A white cat laying on a sofa<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>Keep in mind, sometimes an image is there for purely decorative purposes, so making it visible for screen readers might not be ideal, in those cases, you can use an empty <code>alt</code> to make screen readers ignore this image.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/image.jpg<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><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre>
<p>What should the alternative text contain? Usually, it should contain a brief but concise description of whatās in the image. If itās an image with important information (like a place for an event or a full infographic), this information should also be in alternative text. W3C has created <a href="https://www.w3.org/WAI/tutorials/images/decision-tree/">an alt decision tree</a> to help you decide what information you should add.</p>
<h3 id="width-and-height-attribute"><code>width</code> and <code>height</code> attribute</h3>
<p>One of the most important metrics for performance is <strong>Cumulative Layout Shift (CLS)</strong>, which measures how much a website's content shifts when itās on screen. In other words, a low CLS means the content is pretty stable and will create no problem with user interactivity. You can check more about this concept, you can check <a href="https://web.dev/cls/">this article on web.dev about CLS</a>.</p>
<p>Image loading can create layout shift issues and one way to avoid them is by using the attributes <code>width</code> and <code>height</code> for images. Itāll tell the browser the intrinsic size of the image, and itāll create the imageās aspect ratio when the website is loading, which will reduce the layout shift it can create. This is how itād look in an image.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/image.jpg<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A white cat laying on a sofa<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1200<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>But thatās not all we can do to improve performance on our website with an <code>img</code> tag!</p>
<h3 id="lazy-loading">Lazy loading</h3>
<p>You can have a lot of images on your website, and that can affect its loading time. This is where the concept of <strong>lazy loading</strong> comes in. That means the image will start to load only when itās on the visible part of the viewport. There was a time when this was made with JavaScript but now HTML offers us a way to do it easily by using the attribute <code>loading="lazy"</code></p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/image.jpg<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A white cat laying on a sofa<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1200<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>When you use <code>loading="lazy"</code> in tandem with <code>height</code> and <code>width</code> attributes, youāll make sure the website has enough space for the image at the moment of loading, and at the same time, youāll not load the image unless the user needs it, which is great for performance!</p>
<p>Keep in mind though, if a user has JavaScript disabled, <code>loading="lazy"</code> wonāt work, so you need to keep in mind other performance considerations, but letās put a pin on that topic for now.</p>
<p>Also, keep in mind using <code>loading="lazy"</code> in your Largest Content Painting element (weāll talk about this metric later) will actually <strong>increase</strong> your loading time, so be careful and always test it to know if this attribute gives the intended results.</p>
<h3 id="figure-and-figcaption"><code>figure</code> and <code>figcaption</code></h3>
<p>Sometimes, you need to add additional information that wouldnāt fit in the <code>alt</code>, like image credits, or an important note about it that youād prefer everybody can access to it. You can use the tag <code>figure</code> to wrap the <code>img</code> and then add a caption with the tag <code>figcaption</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figure</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/image.jpg<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A white cat laying on a sofa<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1200<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figcaption</span><span class="token punctuation">></span></span>Persian cats can be recognized by their big hair, round face, and short muzzle<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figcaption</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figure</span><span class="token punctuation">></span></span></span></code></pre>
<p>Keep in mind, <code>figcaption</code> needs to be a direct child of the <code>figure</code> element. Otherwise, it might not be recognized by assistive technologies. This is something Manuel highlights in his article <a href="https://www.matuzo.at/blog/2022/divs-are-bad/">divs are bad!</a>.</p>
<p>Keeping these considerations in mind helps a lot when you create an image, but thatās just the tip of the iceberg. When you start taking into consideration multiple variables like screen sizes, image formats, and user preferences. <code>img</code> tag starts to fall short in power. This is where another tag comes in to add more functionality!</p>
<h2 id="picture-tag"><code>picture</code> tag</h2>
<p>This tag is a container for multiple image sources and depending on the conditions you add to them, the browser will start choosing the right file depending on multiple factors like the viewportās size, image format, pixel density, or user preferences. It acts as a wrapper for the multiple <code>source</code> elements youāll use to determine the best image.</p>
<p>Letās start checking out what we can do with this element.</p>
<h3 id="supporting-different-image-formats">Supporting different image formats</h3>
<p>Unlike <code>.png</code> or <code>.jpg</code> image formats, there are image formats like <code>.avif</code>, <code>.webp</code>, or <code>.apng</code> that you might want to use because they donāt have a loss in quality but have much less weight that a more supported format. However, the previously mentioned image formats have broader support. We can use the <code>source</code> tag to let us use more optimized image formats depending on the browserās support. For this, we can use the attribute <code>type</code> in the <code>source</code> element to indicate the format you need to evaluate.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image.avif<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>image/avif<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image.webp<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>image/webp<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/image.jpg<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A white cat laying on a sofa<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1200<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<p>In this case, itāll start checking if the browser can accept the <code>.avif</code> format, if itās not the case (like Safari before version 16), itāll check if the <code>.webp</code> format is supported. If itās not, itāll show the one in the <code>img</code> element. There are multiple image formats available. If you want to learn more about it, you can check <a href="https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types">MDNās documentation about this topic</a>.</p>
<p>Do you remember I mentioned <code>loading="lazy"</code> might not be enough for performance purposes? This will help us to consider the browserās support to load the most optimal image possible, creating a very robust system to give the user the best image quality possible <strong>and</strong> use as less bandwidth as necessary. Itās the best of both worlds, and thatās possible with just HTML!</p>
<h3 id="responsive-images">Responsive images</h3>
<p>You may want to show a different image or a different part of an image depending on the viewportās size. <code>picture</code> element can help us with that with the <code>media</code> attribute, where we can use a media query to detect the viewportās size and add a different image when needed.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image-mobile.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(max-width: 600px)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/image.jpg<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A white cat laying on a sofa<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1200<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<p>You can use any valid media query there, so for example <code>orientation: portrait</code> and <code>orientation: landscape</code> are valid options when you want to show a different image depending on if the viewportās width is bigger (or smaller) than its width. There are many possibilities you can consider to give the best image to the user.</p>
<h3 id="user-preferences">User preferences</h3>
<p>Now, this is very convenient for performance and user experience, but this also has an important use for accessibility: we can check user preferences to help people with certain disabilities.<br />
For example, some people might have problems like migraines that would prefer to use dark mode, and a very bright image could trigger a migraine attack, so having a version of your image that is better suited for dark mode would be ideal.</p>
<p>Or maybe you have a gif, and someone has disabled animations on their device. You might want to show a static image instead so their experience is not affected. How can we do that?</p>
<p>As I mentioned, you can use any valid media query, that includes media queries that check user preferences like <code>prefers-color-scheme</code> or <code>prefers-reduced-motion</code>. We can even mix them with logical operators to show the appropriate image. Letās check out how it works with an example.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Both dark mode preference and animations are disabled --></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image-dm.jpg<span class="token punctuation">"</span></span><br /> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-reduced-motion: reduce) and (prefers-color-scheme: dark)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Static image when animations are disabled --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-reduced-motion: reduce)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Dark mode preference is checked --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image-dm.gif<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-color-scheme: dark)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token comment"><!-- None of those preferences is active --></span> </span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/image.gif<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A cat dancing<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1200<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<p>Itās very important to note that order matters, itās better to add the tags with more specific media query requirements first to be sure they will not be overwritten by the ones that will appear later. Also, keep in mind you can combine all you have seen, so there are a lot of possibilities with <code>picture</code> and <code>source</code> elements.</p>
<p>That covers mostly everything you should consider but there are a couple of points you can take into account.</p>
<h2 id="additional-considerations">Additional considerations</h2>
<h3 id="pixel-density-and-srcset">Pixel density and <code>srcset</code></h3>
<p>The attribute <code>srcset</code> you use in the picture element has a slightly different syntax if you want to give a better image depending on the deviceās pixel density. For this, we can add a list of images separated by commas and after the imageās URL, weāll put the type of pixel density we want to support with this image. Letās take a look at it with an example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span><br /> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image-mobile.jpg 1x,<br /><span class="highlight-line"> path/to/image-mobile-2x.jpg 2x,</span><br /> path/to-image-mobile-3x.jpg 3x<span class="token punctuation">"</span></span><br /> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(max-width: 600px)<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"><</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>path/to/image.jpg<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A white cat laying on a sofa<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1200<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image-2x.jpg 2x,<br /> path/to/image-3x.jpg 3x<span class="token punctuation">"</span></span><br /> <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></span></code></pre>
<p>There is something I didnāt mention and that is the fact you can use the <code>srcset</code> attribute in the <code>img</code> element, and it works great when you want to support images depending on the deviceās pixel density. Next time to want to bring better quality images for images depending on the device, you now know how to do that!</p>
<h3 id="preload-image-to-improve-lcp">Preload image to improve LCP</h3>
<p>Do you remember when I mentioned CLS? There is another metric called <a href="https://web.dev/lcp/">Largest Content Paint (LCP)</a> which is a metric that measures how much it takes to load the biggest image (or text block) to render in the viewport. Commonly, this LCP is a big image. We can use other image formats to decrease the LCPās size, but what if thatās not enough? We have the alternative of preloading the image in the <code>head</code>, so it'll make the image renders faster, and by extension, improve this metric. This is how itād look in our <code>head</code> tag.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preload<span class="token punctuation">"</span></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>path/to/image.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">as</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span></code></pre>
<p>You donāt want to abuse this technique though, because otherwise, itāll <strong>increase</strong> the page load time. Just use it for your most important image(s) and test performance accordingly to check if itās improving those metrics.</p>
<h3 id="decoding="async""><code>decoding="async"</code></h3>
<p>I mentioned <code>loading="lazy"</code> before but there is another HTML attribute we can use to optimize a websiteās loading time. While <code>loading="lazy"</code> decides <strong>when</strong> the image will be loaded (at the moment that it is close to the viewport), we have another attribute that defines <strong>how</strong> itāll be loaded</p>
<p>Normally, all our site will be loaded synchronously because the browser reads HTML line by line, so if it finds a big image, the rest of the site wonāt download until the image is loaded. This is where <code>decoding="async"</code> enters to give a hint to the browser to try to download the image and at the same time downloading another part of the site.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>A white cat laying on a sofa<span class="token punctuation">"</span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1200<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>800<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span> </span><br /><span class="highlight-line"> <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span></span><br /><span class="token punctuation">/></span></span></code></pre>
<p>Using both, <code>decoding="async"</code> and <code>loading="lazy"</code> in an image, itāll help to reduce greatly a websiteās loading time, and <code>decoding</code> is a highly supported attribute.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Adding an image to a website is easy⦠But things start to complicate when you start to keep in mind performance and accessibility considerations. Luckily for us, HTML can give us the proper tools to tackle those considerations with no problem at all.</p>
<p>I chose this topic for this Advent Calendar for a reason and itās because it made me notice how powerful, robust, and surprisingly complex HTML is. Seeing what HTML has to offer just to bring the right image to your screen with relatively low effort was fascinating and it started me to make dig deeper into what HTML can do.</p>
Modern HTML as a foundation for progressive enhancement
2022-12-17T00:00:00Z
https://htmhell.dev/adventcalendar/2022/17/
<p>Reading HTMHell, you might be aware that progressive enhancement is a thing. To sum things up, it's a way to make sure anyone gets a viable version of your page whatever is their context ā slow bandwitdh, oldish browser, etc. ā but also making the said page more resilient (e.g. to JavaScript errors).</p>
<p>Progressive enhancement starts from the ground up:</p>
<ol>
<li>content,</li>
<li>marked up with HTML,</li>
<li>styled with CSS,</li>
<li>enhanced with JavaScript</li>
<li>and improve accessibility with a bit of ARIA.</li>
</ol>
<p>Each of those steps should work as-is and enhance the lower steps without breaking them. In other words, you would need to write your markup independently from the CSS or JS you will apply later on.</p>
<p>Great.</p>
<p>That being said, it's kinda obvious that the better you know each step in the stack the more robust your page should be. JavaScript is everywhere so we may assume you know about it. ARIA is getting some shedlight for a few years now, so even without knowing ARIA we may suppose you use components libraries or something doing this just fine. CSS is constantly improving, I suppose you're following and discovering new CSS stuff every day or so.</p>
<p>But what about HTML? Is it part of your technical watch? How frequent are new HTML stuff popping out in your information sources? Not so often, I guess.</p>
<p>But hey. HTML is a living standard so specifications do change, browsers always improve support for HTML āas part of <a href="https://wpt.fyi/interop-2022">Interop 2022</a> for exampleā so there are new kids in town, either specified or implemented in browsers, and some of them are meant to be native controls replacing long-standing <code><div></code>-based JavaScript components.</p>
<p>What if we could improve the HTML stack for such components, making the markup step more resilient?</p>
<p><strong>Disclaimer</strong>: you need to think of HTML as a foundation, not a replacement for your existing JavaScript component. If your pattern needs ARIA, do not try to implement a low-elevel component. In other word, always try to provide the best possible user experience and don't care too much about developer experience. You may be interested in <a href="https://adrianroselli.com/2022/10/under-engineered-patterns-for-a11ytoconf.html#References">Adrian Roselli's under-engineered components blog posts serie</a>.</p>
<p>Let's go for a ride!</p>
<h2 id="lessdetailsgreater-and-lesssummarygreater-as-a-aria-disclosure-pattern-foundation"><code><details></code> and <code><summary></code> as a ARIA disclosure pattern foundation</h2>
<p>Think of those "Show more" button thinggies where activating it make the content below appear. Making such component accessible currently require you to implement <a href="https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/">the ARIA disclosure design pattern</a>, basically using a button with <code>aria-expanded</code> attribute to communicate state.</p>
<p><code><details></code> and <code><summary></code> are a really good fit for this. As their names are pretty obvious, this markup pattern will display a summary and hide detailsābut make details available when activating the summaryā and the <a href="https://thepaciellogroup.github.io/AT-browser-tests/test-files/details-summary.html">browsers implementation</a> and <a href="https://caniuse.com/details">support</a> are really good.</p>
<p>This feels quite right, isn't it? So why bother with the disclosure pattern at all and not use details and summary all the way? Well, interactions are not exactly what's expected by your users and support in browser and assistive technologies is not straightforward yetā <a href="https://www.scottohara.me/blog/2022/09/12/details-summary.html">as Scott O'Hara pointed out</a>.</p>
<p>But⦠may we use it as our foundation for an ARIA disclosure pattern? <a href="https://codepen.io/ffoodd/pen/XWYmmGj">Let's figure it out in a dedicated CodePen</a>!</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="XWYmmGj" data-user="ffoodd" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/ffoodd/pen/XWYmmGj">
Enhanced <details> with ARIA disclosure</a> by ffoodd (<a href="https://codepen.io/ffoodd">@ffoodd</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<p>Not much magic here: to improve and harmonize AT support, we make it a disclosure by adding to <code><summary></code> a button role and <code>aria-expanded</code> to convey state and make sure the disclosed content is hidden with the eponymous attribute. The <code>open</code> attribute on <code><details></code> handles this natively but <a href="https://www.scottohara.me/blog/2022/09/12/details-summary.html#impact-of-removing-the-default-disclosure-triangle">we can't rely on it as long as we override the default triangle marker</a> āwhich we will undoubtedly do: we must fully implement the disclosure pattern to prevent inconsistent rendering in browsers and ATs.</p>
<p>Moreover, did you know Chromium-based browsers allow collapsed details content discoverability using browser search? That's something coming to HTML with <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden#the_hidden_until_found_state">the <code>until-found</code> value for the <code>hidden</code> attribute</a>, but that's not supposed to be the case of disclosed content when using the ARIA pattern.</p>
<p>As you may have understood, using <code><details></code> and <code><summary></code> enhanced with the ARIA disclosure pattern makes little to no difference for most users⦠but whenever your JS or CSS fails, your semantic markup would take over and stay interactiveāwith all its current limitations in ATs.</p>
<p>Also please be careful using those elements as a foundation: they aren't a good fit <a href="https://daverupert.com/2019/12/why-details-is-not-an-accordion/">for accordions</a>, <a href="https://melsumner.github.io/details-as-a-menu">a flyout menu</a>, <a href="https://cloudfour.com/thinks/a-details-element-as-a-burger-menu-is-not-accessible/">a burger menu</a>, <a href="https://adrianroselli.com/2019/04/details-summary-are-not-insert-control-here.html">a tab set or a dialog</a>⦠Stay semantic!</p>
<p>What if I told you there's more progressively enhanceable HTML elements like those? Wanna have a look?</p>
<h2 id="lessoutputgreater-as-a-live-region"><code><output></code> as a live region</h2>
<p>Ever needed to populate a live region to communicate content changes to screen reader usersāoften being <a href="https://kittygiraudel.com/2016/10/13/css-hide-and-seek/">visually hidden with CSS</a>? That's quite common now, and you find live region handling in some frameworks: <a href="https://material.angular.io/cdk/a11y/overview#liveannouncer">Angular has a <code>LiveAnnouncer</code> in its Accessibility CDK</a>, <a href="https://make.wordpress.org/accessibility/handbook/markup/wp-a11y-speak/">WordPress has it <code>wp.a11y.speak()</code> internal script</a>⦠<a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions">ARIA live regions rely on three specific roles: <code>status</code>, <code>log</code> and <code>alert</code></a>, each having a slightly different behaviour.</p>
<p>But did you know there's an HTML element that's a native live region, with an implicit <code>status</code> role? <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output">Meet <output></a>! Its <a href="https://thepaciellogroup.github.io/AT-browser-tests/test-files/output.html">browser implementation is complete</a> and <a href="https://caniuse.com/mdn-html_elements_output">support is excellent</a>. However some specific browsers and ATs pairing are flawed, as <a href="https://www.scottohara.me/blog/2019/07/10/the-output-element.html">Scott O'Hara (again) pointed out</a>.</p>
<p><a href="https://codepen.io/ffoodd/pen/rNvqGBB">Let's make <code><output></code> more robust by making it an ARIA live region, on CodePen</a>!</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="rNvqGBB" data-user="ffoodd" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/ffoodd/pen/rNvqGBB">
Enhanced <output> with status role</a> by ffoodd (<a href="https://codepen.io/ffoodd">@ffoodd</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<p>As you may remember, expliciting HTML elements roles was quite needed a few years ago for landmarks mostly to improve support in browsers and ATs āand still recommended in some accessibility guidelines like <a href="https://www.numerique.gouv.fr/publications/rgaa-accessibilite/methode-rgaa/criteres/#crit-12-6">the French RGAA</a>. We got used to write <code><main role="main"></code>, <code><header role="banner"></code> and so on. What's also good with <code><output role="status"></code> is that it allows you to style your live regions by selecting the output element: lower specificity, and some kind of soft reset too āCSS containment, maybe?</p>
<h3 id="a-more-specific-live-region-toast!">A more specific live region: toast!</h3>
<p>There's also a case for visually displayed live regions: toasts! I won't go way further on the topic, because it's been explored in a wide variety of posts: <a href="https://www.scottohara.me/blog/2019/07/10/the-output-element.html">Scott O'Hara already mentionned <cite>The output element</cite></a>, a technical follow-up of his <a href="https://www.scottohara.me/blog/2019/07/08/a-toast-to-a11y-toasts.html"><cite>A toast to an accessible toastā¦</cite> addressing UX concerns)</a>, and <a href="https://adrianroselli.com/2020/01/defining-toast-messages.html">Adrian Roselli in <cite>Defining toast messages</cite></a>.</p>
<p><strong>Warning</strong>: if your toast or live region contains interactive elements, it should probably use a <code>dialog</code> or <code>alertdialog</code> role, and preferably not an <code><output></code> element at all. But⦠there might be another HTML element for that!</p>
<h2 id="a-modal-lessdialoggreater">A modal <code><dialog></code></h2>
<p>I won't go that deep with <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog">the <code><dialog></code> element</a>, but let's explore what it does natively compared to <a href="https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/">the ARIA dialog design pattern</a>:</p>
<ul>
<li>it has an implicit <code>dialog</code> role,</li>
<li>when opened with the <code>.showModal()</code> method:
<ul>
<li>it has an implicit <code>aria-modal="true"</code> property,</li>
<li>it's closable with the <kbd>Escape</kbd> key,</li>
</ul>
</li>
<li>it traps focus in the dialog ābut allows to get out of the browser frameā¦</li>
<li>it restores focus to the opening element when closing the dialog,</li>
<li>it makes the document <a href="https://developer.mozilla.org/docs/Web/API/HTMLElement/inert"><code>inert</code></a> automatically.</li>
</ul>
<p>And there's more:</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::backdrop">the <code>::backdrop</code> pseudo-element</a> allows you to style your dialog overlay really easily,</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:modal">the <code>:modal</code> pseudo-class</a> applies when opening dialog with the <code>.showModal()</code> method,</li>
<li>if your dialog contains a form, using <code>method="dialog"</code> form attribute makes your submit button to close the dialog.</li>
</ul>
<p>Dialog element on its own isn't consistent enough in browsers and ATs (yet), <a href="https://www.scottohara.me/blog/2019/03/05/open-dialog.html">as Scott O'Harra (again) pointed out</a>, but you got the idea: what about enhancing it by applying the ARIA dialog pattern to it, and relying on its standard behaviour to save a few lines of JavaScript? That's more or less what <a href="https://web.dev/building-a-dialog-component/">Adam Argyle explored in his dialog component pattern on web.dev</a> with some nice little tricks to learn āhowever keep in mind <a href="https://twitter.com/aardrian/status/1586792250113232896">web.dev patterns aren't battle-tested with ATs nor against WCAG</a>. <a href="https://css-tricks.com/dialog-components-roll-your-own/">Building a dialog component is not straightforward</a>, and <a href="https://www.smashingmagazine.com/2021/07/accessible-dialog-from-scratch/">Kitty Giraudel goes in details about what's needed to make an <cite>accessible dialog from scratch</cite></a>. Since those posts, the dialog support has improved quite a lot and is even <a href="https://wpt.fyi/interop-2022">part of Interop 2022</a>, so expect it to improve again this year's last month.</p>
<p>This is probably the least obvious case, since speaking of progressive enhancement the <code><dialog></code> element might be a bad choice: it simply cannot work without JavaScript. But if your JavaScript fails for any reason, your event handlers might still work.</p>
<h2 id="conclusion">Conclusion</h2>
<p>HTML is awesome. It's obviously not enough to provide a fully accessible experience to users, as <a href="https://daverupert.com/2020/02/html-the-inaccessible-parts/">Dave Rupert highlighted two years ago in <cite>HTML: the inaccessible parts<cite></cite></cite></a> ā even if some of those examples are not accurate anymore, fixed in either browser or ATs.</p>
<p>But not being perfect is probably still better than being neutral, isn't it?</p>
<p>I think that's the kind of thing HTML unveils: at some point, details and summary shall be consistently implemented and exposed to ATs, making ARIA enhancements pointless. Scott's work, from advanced testing to blog posts and filing bugs, are the way forward. Fully implemented patterns in libraries like <a href="https://a11y-dialog.netlify.app/">Kitty Giraudel's a11y-dialog</a> are what alllows maturity and clarify expectations. And as usage increases, browsers and ATs will eventually consider prioritization, in Interop or <a href="https://aria-at.w3.org/">ARIA-AT</a>.</p>
<p>Let's use, test and help browsers and ATs support those newer HTML elements!</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
5 HTML elements, And a partridge in a despair tree
2022-12-16T00:00:00Z
https://htmhell.dev/adventcalendar/2022/16/
<p>HTML is a beautiful programming language. It comes with many out-of-the-box accessibility benefitsāit conveys semantic meaning to assistive technology, enabling people to consume content and complete often important journeys that they may not be able to do outside of the web. So why is all that goodness we can get for free ignored so often?</p>
<p>To expand on that point a little, why do we ignore native HTML elements that will tell people exactly what their purpose is and enable them to interact with it, and use overly complex ARIA-laden JavaScript solutions? Some of this may come down to how something is designed, while in other cases it may be misunderstanding/confusion or copied-and-pasted bad practices. In this article, we'll look at 5 common instances when semantic HTML is ignored.</p>
<h2 id="lessbuttongreater"><code><button></code></h2>
<p>Let's start with a topic that people will have seen many discussions aroundāinteractive buttons. Take a look at the <a href="https://www.htmhell.dev/">latest postings on the HTMLHell site</a>, which will no doubt feature this issue heavily!</p>
<p>A button is often used to toggle or activate some dynamic functionality for a user. That could be to reveal a menu with important pages included, submit a form, toggle expandable content, and so on.</p>
<p>HTML includes the <code><button></code> element. This tells people that it can be interacted with and can help communicate the state of a component. By default, it is focusable, has key events, and comes with some browser default styles.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I'm a real button<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Here we have some default browser styling making it <em>look like a button</em>, have focus behavior handled for us, and have key events mapped to it (<code>click</code>, <code>keyup</code>, <code>keydown</code>, etc) making it <em>act like a button</em>. Because <strong>it is a button!</strong></p>
<style>
button, button:hover, button:focus-visible { all: revert }
</style>
<div data-demo="demo">
<button class="button1" type="button">I'm a real button</button>
</div>
<script>
const button0 = document.querySelector('.button1')
button0.addEventListener('click', e => {
alert("š¤")
})
</script>
<p>Unfortunately, a pattern that is all too commonāand used in the wrong circumstancesāis to <a href="https://benmyers.dev/blog/clickable-divs/">turn a non-interactive element into a faux button</a>.</p>
<p>This will often be achieved with an ARIA role:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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 punctuation">></span></span>I'm not a button<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="demo">
<div role="button" tabindex="0" class="button2">I'm not a button</div>
</div>
<script>
const button2 = document.querySelector('.button2')
button2.addEventListener('click', e => {
alert("š")
})
</script>
<p>This <code><div></code> (a non-semantic element designed for containing things) would now be announced as an interactive element by assistive technology, this is all well and good, but is it robust and accessible? No.</p>
<p>We're telling assistive technology that it's a button, but providing none of the functionality that comes with the native element.</p>
<p>As well as the role, we'd also then need to handle focus behavior manually using JavaScript and the <code>tabindex</code> attribute and also <a href="https://adrianroselli.com/2022/04/brief-note-on-buttons-enter-and-space.html">handle key events</a>. This adds unnecessary complexity and requires the user to download JavaScript just to do something that could be done with a native element.</p>
<h3 id="extra-considerations">Extra considerations</h3>
<p>Whilst using a native button is a great start, there are other things to consider. Does your button have a suitable accessible name? (especially if it's an icon button). If the button has the purpose of taking a person to another page, it should be a link (<code><a></code>).</p>
<p>The following links are great resources for helping create a great button (or link).</p>
<ul>
<li><a href="https://css-tricks.com/a-complete-guide-to-links-and-buttons/">A complete guide to Links and buttons</a> on CSS-Tricks</li>
<li><a href="https://adrianroselli.com/2016/01/links-buttons-submits-and-divs-oh-hell.html">Links, Buttons, Submits, and Divs, Oh Hell</a> by Adrian Roselli</li>
</ul>
<h2 id="lessselectgreater"><code><select></code></h2>
<p>A <code><select></code> is a control that provides a menu of options.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>select<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sort by<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</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>select<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>popularity<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>By popularity<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>price-low-high<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>By lowest price<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>price-high-low<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>By highest price<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="demo">
<label for="select">Sort by</label><br />
<select id="select" name="select">
<option value="popularity">By popularity</option>
<option value="price-low-high">By lowest price</option>
<option value="price-high-low">By highest price</option>
</select>
</div>
<p>When a person changes the option of the select, the value of it then becomes the value of the new option, nice! All browsers support this control and <a href="https://a11ysupport.io/tech/html/select_element">assistive technology support</a> is strong too. Screen readers announce the control's accessible name (our label in this example), how to open the select menu, the list of options, and then the value when selected.</p>
<p>For what is an important interaction for many products, it's surprising how many custom-built āselectā controls there are in the wild. This normally comes down to a few reasons, all fairly related to each other:</p>
<ul>
<li>The design has come through and included elements other than text in the select options (checkboxes, images, multi-select functionality)</li>
<li>Select controls can be notoriously difficult to style, and the options even more so</li>
<li>Rendering inconsistencies between browsers/OS: Although the functionality remains consistent, a select and its states can look very different from one browser to another and especially with their options showing</li>
</ul>
<h3 id="why-not-fake-a-select-though?">Why not <em>fake</em> a select though?</h3>
<p>It is of course possible to build something that looks and behaves like a <code><select></code> with other elements and a sprinkle of JavaScript and ARIA. It's also possible to make this into a robust and accessible solution; for example, <a href="https://24ways.org/2019/making-a-better-custom-select-element/">Julie Grundy's article</a> from 24 WAYS. This example uses semantic HTML and is careful in its usage of ARIA.</p>
<p>Whilst it may be doable, let's consider a few reasons it might still be better to suggest and use the native <code><select></code>.</p>
<h4>Future developer readability</h4>
<p>Whilst a more custom and complex approach may make sense to one developer who has followed an example/tutorial to build the component, a developer picking up bugs/changes/feature requests in the future may not have as good of an understanding of how all the elements have been set up and what their roles are supposed to be.</p>
<p>Although this could be solved with sufficient documentation, it's still extra work for somebody else to get up to speed with.</p>
<h4>Misconfiguration</h4>
<p>When using ARIA to add a role to an element to try and improve assistive technology communication, it can often require very specific roles to be included in parent/child element relationships. If misconfigured, not only will the whole component not announce correctly, but it's breaking accessibility and might cause more problems than it was used to solve!</p>
<p>The more complex and ARIA-heavy these custom components areāa custom select being a good example of one that could be with enough featuresāthe more chance of this misconfiguration happening, especially concerning the previous point of somebody with less understanding picking up changes.</p>
<h4>Communicate with designā'shift left'</h4>
<p>Many accessibility issues can be solved during the design phase. The barriers we create are designed in, and the more we can shift the conversation and responsibility of accessibility to earlier in the product lifecycle, the better (often referred to as shifting left).</p>
<p>Constant and efficient communication between designers and developers can play a huge part in this. During sessions such as a design-developer handover, or <a href="https://www.figma.com/community/file/953682768192596304">accessibility annotation pairing</a>, it could be a great time to have a conversation about how a certain component design might lead to extra complexity, and to see if a native HTML element might be able to fit into the design.</p>
<p>Not only could this help educate a designer when it comes to semantics and accessibility considerations for future work, but it could also stop accessibility issues from getting to development or even to further down the line, when fixes/changes become much more expensive, or, even worse, end up unintentionally excluding people in production.</p>
<h3 id="help-on-the-way?">Help on the way?</h3>
<p><a href="https://open-ui.org/">The Open UI project</a> is trying to make it easier for designers/developers to both use native controls for elements like <code><select></code> and to have the styling and functionality freedom that often turns people to use more complex custom solutions that can be inaccessible:</p>
<blockquote>The purpose of Open UI to the web platform is to allow web developers to style and extend built-in web UI controls, such as <code><select></code> dropdowns, checkboxes, radio buttons, and date/color pickers.
To do that, we'll need to fully specify the component parts, states, and behaviors of the built-in controls, as well as necessary accessibility requirements, and provide test suites to ensure compatibility. We'll also implement polyfills for our extensible web UI controls.
Whilst the project is still in its infancy, concerning the <code><select></code> element, there have been some developments with <code><selectmenu></code>. Whilst not ready for production yet, it can be <a href="https://open-ui.org/prototypes/selectmenu">enabled in Chromium-based browsers with a flag</a>. <code><selectmenu></code> allows greater customisation of the select control and the options that sit inside of it.</blockquote>
<p>You can read more about it in Hidde's article <a href="https://htmhell.dev/adventcalendar/2022/13/">āOne day we'll have a fully customisable selectā</a>.</p>
<h2 id="lessnavgreater"><code><nav></code></h2>
<p>Navigation allows users to complete important journeys and find the key information they may need.</p>
<p>HTML includes the <code><nav></code> element. This is <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/roles/landmark_role">a landmark element</a> and means it will automatically communicate to assistive technology that it's an element being used for navigation links (the same as adding <code>role="navigation"</code>).</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>Main<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/about<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>About Us<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/history<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Our History<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/contact<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Contact Us<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<p>This is a perfectly accessible example of some navigation. <strong>No ARIA is needed</strong>. However, navigation can often require multiple levels of nested dropdowns, and become more complex. The example can still add this and be accessible, which we will take a look at, but first let's look at a pattern often reached for when it comes to navigation with dropdowns - <code>role="menu"</code>.</p>
<p>Whilst the naming of <code>role="menu"</code> and the child roles it requires ( <code>role="menuitem"</code>) may suggest that it'd be perfect to use for site navigation, the purpose of it is very different. This pattern is more for replicating desktop application menus. <a href="https://adrianroselli.com/2017/10/dont-use-aria-menu-roles-for-site-nav.html">Adrian Roselli has a superb article</a> explaining the difference.</p>
<p>You can see an example of how this would be marked up on the <a href="https://www.w3.org/WAI/ARIA/apg/example-index/menubar/menubar-navigation.html">aria authoring practices site</a>. Whilst there are cautionary recommendations at the top of the page detailing how this is unsuitable for site navigation, the page goes on to use it for one. This is an example of how just using these resources as a copy-and-paste solution can lead to bad practices.</p>
<p>Going back to how we can use <code><nav></code> and more semantic elements to start to create site navigation with multiple levels:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>Main<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">aria-current</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page<span class="token punctuation">"</span></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>Home<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>About Us<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><br /><span class="highlight-line"> <span class="token attr-name">type</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><br /><span class="highlight-line"> <span class="token attr-name">aria-expanded</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><br /><span class="highlight-line"> <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>about-us-submenu<span class="token punctuation">"</span></span></span><br /> <span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Visual cue icon - for example a downward facing chevron --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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>true<span class="token punctuation">"</span></span> <span class="token attr-name">focusable</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 attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- Icon SVG code --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Toggle About Us submenu<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</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>about-us-submenu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Our history<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Meet the team<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Work<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Contact<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<p>Here we're using nested unordered lists to mark up the second level of navigation, meaning that visually and semantically, the different levels of the menu item are related to each other, even without CSS and JavaScript loading.</p>
<p>You might be asking ābut there is ARIA in there now, and you said we didn't need any?ā. This is true, but we can use ARIA in the appropriate way to <strong>enhance</strong> the experience for people using assistive technology; we just shouldnāt solely rely on its <code>role</code> attribute to communicate that it's a navigation in the first place. Of course, JavaScript will also be needed to toggle some of these attributes, but, by default, without any of this our markup is semantic and accessible.</p>
<p>For a more detailed guide to building a semantic and accessible navigation, check out <a href="https://web.dev/website-navigation/">the web.dev navigation article by Manuel</a>.</p>
<h2 id="lessul/olgreater"><code><ul/ol></code></h2>
<p>Lists (ordered and unordered) are very common elements used on sites. They both have semantic HTML elements:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Unordered list item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Another unordered list item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Yet another unordered list item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="demo">
<ul>
<li>Unordered list item</li>
<li>Another unordered list item</li>
<li>Yet another unordered list item</li>
</ul>
</div>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ol</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Ordered list item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Another ordered list item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>Yet another ordered list item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ol</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="demo">
<ol>
<li>Ordered list item</li>
<li>Another ordered list item</li>
<li>Yet another ordered list item</li>
</ol>
</div>
<p>They can help with things such as visually and semantically breaking down complex points, and they can give expectations of important steps to complete or consider. Communicating that a list with several items (and possibly links) exists can be important to people using assistive technology; for example, it can communicate how many items are in the list.</p>
<p>A common mistake that can be made is to just visually represent a list with a combination of other HTML elements; for example (<a href="https://htmhell.dev/30-bullet-list/">taken from HTMLHell</a>):</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ⢠HTML</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> ⢠CSS</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> ⢠JavaScript</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>Whilst this may visually represent a list, it is going to give people using assistive technology a very different experience. Each 'item' has no relation to the other. As it's just one paragraph element, this would give people no indication of how many items were present and how they relate in context and the order if they needed to be read sequentially.</p>
<h3 id="who-would-do-that-though?">Who would do that though?</h3>
<p>Unordered lists were amongst the <a href="https://www.webdesignmuseum.org/web-design-history/tim-berners-lee-published-a-document-called-html-tags-1991">first 18 HTML tags released</a> so why do people still ignore them over just visually representing a list? And why are they so important? Aside from poor markup choices, there could be a few other reasons for this issue occurring.</p>
<h4>CMS WYSIWYG output</h4>
<p>It may not be apparent to somebody how to add lists in a content management system WYSIWYG editor, thus causing them to just visually create the list of items (for example using the bullet point shown in the previous example).</p>
<h4>Content editor skill gaps</h4>
<p>When content editors add content in a CMS it may be that they have misunderstood that using return to add items to a newline does not create a list, but it might visually appear to look like one to them in previews, etc.</p>
<h4>Markdown/other language confusion</h4>
<p>In some content language formats, such as markdown, lists can be added by using the hyphen/asterisk to indicate a list item, and these are then translated into correct list markup. It could be that somebody has mistakenly tried this thinking it might be the case.</p>
<h4>CSS advancements</h4>
<p>Ok, so this is an HTML-focused article, but let's not forget that CSS is amazing! What we can do with it in modern web development is incredible. It also makes being able to use semantic elements easier/more appealing if it was styling reasons they were not used for in the first place.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::marker">The <code>::marker</code> pseudo element</a> enables styling on the bullet point of an u <code>ul</code>, whether it just be changing the colour of it, or adding custom content to it.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@counter-style">There's also the <code>@counter-style</code> rule</a>. This enables the usage of non-standard bullet types but also can convert this to a string, meaning better accessibility considerations.</p>
<p>For ordered lists <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/counter-reset">the counter reset property</a> can help with custom styling numeric counters.</p>
<h3 id="slight-tangent-incoming!">Slight tangent incoming!</h3>
<p>Educating content editors on using semantic markup can be an important part of ensuring that semantic, accessible content is being added to a site. Despite designing and building an accessible experience, this work could be undone by fundamental knowledge gaps when adding content such as blog posts, case studies, etc if they are handled through a CMS. Link text, image alt text, heading structure, correct element usage, etc. can all have detrimental effects on a person's experience.</p>
<p>If the importance of using semantic and accessible elements and structure is lost on somebody, it can be useful to remind people that it can have many benefits; for example, SEO and readability. Jeremy Keith has an interesting thought on <a href="https://adactio.com/journal/18199">doing the right things for the wrong reason</a>.</p>
<p>End of tangent.</p>
<h3 id="lists-and-safari-"list"-itis">Lists and Safari "list"-itis</h3>
<p>Something to consider when using lists and testing behavior with iOS/MacOS and Safari and VoiceOver, is the removal of default styles (<code>list-style: none;</code>). With this combination of OS and browser, this style will remove the semantics using the <code><ul></code> or <code><ol></code> element provides. The reasoning behind this comes from the overuse of lists to mark components up.</p>
<p>A bug was filed for this 'issue' which prompted the following response:</p>
<blockquote>This was a purposeful change due to rampant ālistā-itis by web developers. ⦠Basically, if you remove all default visible indication of the list, there is no indication to a sighted user or screen reader user that the content is a list. If you want to override this heuristic for accessibility, you can always add an explicit ARIA role=ālistā.
As suggested to get around this, you can explicitly add a role to the element:</blockquote>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- List items --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<p>Although this seems verbose (it'll also get flagged when validating HTML) and is an extra <em>thing</em> to remember, it'll make the experience much better for people using VoiceOver and encountering a list.</p>
<p><em><strong>Note</strong></em>: This is not needed when the <code>ul</code> is the child of a <code>nav</code> landmark element. In this case the <a href="https://twitter.com/cookiecrook/status/1612487416346193921">semantics of the list elements are preserved</a>(disclaimer: links to a Twitter thread), even if default styling is removed. Another great reason to make sure navigation is marked up semantically and accessibly.</p>
<p>So still announce list semantics if list was custom styled.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>Main<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/about<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>About Us<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/history<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Our History<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/contact<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Contact Us<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="lessfieldsetgreater"><code><fieldset></code></h2>
<p>Forms are a critical part of many journeys on a site, but yet they are so often marked up inaccessibly or use patterns that do not take user needs into account. There are also many examples I've seen where native elements have been ignored in favor of overly complex solutions.</p>
<p>The <code><fieldset></code> and <code><legend></code> elements are a great way to break up more complex forms, and can greatly improve the flow and experience of filling a form out for many people:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>How would you like to hear from us?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment"><!-- Checkboxes with selection options --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code><fieldset></code> element implicitly communicates a role of <code>group</code>āuseful for grouping logically related items-and the <code><legend></code> provides the 'caption' or accessible name for it thus, communicating to assistive technology such as screen readers that the fields within are related and providing a label to announce.</p>
<p>Here's an example seen recently on a documentation site of using non-semantic elements to do this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>my-radio-group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Picked<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-radio-group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Field</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>picked<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>One<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> One</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Field</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>picked<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Two<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> Two</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>Picked: {values.picked}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>This would result in a very similar experience to the fieldset and legend pattern but is just doing more work to get there. It's using non-semantic elements and using ARIA to turn them into something that can be done natively!</p>
<p>The issue with examples like this being included on documentation sites is that it runs the risk of being copied and pasted as an example, and then becoming habits in that codebase.</p>
<p>Other benefits native elements can offer would also have to be manually handled; for example, <code><fieldset></code> can take a <code>disabled</code> attribute, which would make all child input controls disabled.</p>
<h3 id="other-form-element-tips">Other form element tips</h3>
<p>OK, so these do push this article over the 5 elements specified in the title. <em>However</em>, due to the number of times these issues come up on forms, and how low-hanging, high-impact they can be to fix, let's quickly mention them.</p>
<h4>Accessible input names</h4>
<p>Don't rely solely on placeholder attributes for input controls that require people to enter text/numbers etc. These can often fail colour contrast requirements, have patchy support for assistive technology, and aren't translated if internationalisation is required.</p>
<p>One solution could be to add an <code>aria-label</code> to the control, as this would provide an accessible name; however, even better would be to include an associated visual label for the control. This has benefits for many user groups.</p>
<p>Do also try to steer clear of the 'floating label' pattern. Although it does seem to strike a great balance between the designed 'placeholder' look and having a label, it can often introduce accessibility issues of its own, such as hard-to-read text and leaving the input feeling crowded.</p>
<h4>Validation messaging</h4>
<p>Ensure that colour alone is not being relied on to convey meaning in validation messaging. It can be nice UX (user experience) to include a suitable icon along with the message, for example, an exclamation with an error. If this can't be achieved, <a href="https://design-system.service.gov.uk/components/error-message/#be-clear-and-concise">ensure messaging is well-written</a>.</p>
<p>Make sure that the messaging is suitably announced to assistive technology. This can be done via an <a href="https://tink.uk/accessible-forms-with-aria-live-regions/">ARIA live region</a>. When we think about errors associated with inputs, in particular, not having this surfaced immediately could create barriers for people relying on using a form to complete a journey/access a vital service.</p>
<p><strong>Note</strong>: Although live regions are a great idea for important or time sensitive content like form errors. Be careful overusing it, it can become very 'noisy' and disruption on a person's journey.</p>
<h2 id="summary">Summary</h2>
<p>In this article, we've covered only a small amount of the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element">HTML elements available</a>. Each case reviewed how using a native HTML element can reduce complexity when building a component and the need for having to manually handle a range of possible interactions people using different input methods need to use, which requires JavaScript.</p>
<p>When reviewing a design, consider if you might be able to use a native HTML element to achieve the functionality, or consider having a conversation with design to see if something could change to allow you to do so. Communications such as accessibility annotation sessions can be useful for this.</p>
<p>HTML can do a lot, without needing to reach for and ship large chunks of JavaScript and potentially exclude whole groups of people interacting with a component. It's robust, semantic, and accessible. Take time to learn it, and where and when to use it.</p>
<h2 id="further-reading">Further reading</h2>
<p>Some useful links to help learn more about using semantic and accessible HTML. Also how to use ARIA responsibly if needed.</p>
<ul>
<li><a href="https://web.dev/learn/html/">web.dev - learn HTML</a></li>
<li><a href="https://web.dev/learn/accessibility/">web.dev - learn accessibility</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Glossary/semantics#semantics_in_html">Why semantics matter</a>āreasoning and examples, should you need it</li>
<li><a href="https://inclusive-components.design/">Inclusive Components</a>āprogressive, robust, semantic, and accessible common component patterns from Heydon Pickering</li>
<li><a href="https://www.w3.org/TR/aria-in-html/">Using ARIA</a>āincluding the 5 rules of ARIA</li>
<li><a href="https://www.w3.org/WAI/ARIA/apg/">ARIA authoring practices guide</a></li>
</ul>
Get that marquee āØAeStHeTiCāØ
2022-12-15T00:00:00Z
https://htmhell.dev/adventcalendar/2022/15/
<p>With the current <a href="https://www.urbandictionary.com/define.php?term=Y2K%20aesthetic">Y2K fashion trend</a> and JLo being back together with Ben Affleck, the 2000s are having a revival this year. Many brands are jumping onto the boat by creating websites with an āold-schoolā vibe.</p>
<p style="margin-bottom: 0;">
<img src="https://htmhell.dev/images/advent2022/14/sepronic_world.jpg" alt="SEPRONIC WORLD by Thea Wood on Behance" style="display: block" width="740" height="246" />
</p>
<p><a href="https://www.behance.net/gallery/116549859/SEPRONIC-WORLD?tracking_source=search_projects_recommended%7Cwindowsxp"><em>SEPRONIC WORLD by Thea Wood on Behance</em></a></p>
<p>The <code>marquee</code> element is an old web dev community favourite, fitting the 2000s aesthetic. However, this HTML element is deprecated and should not be used on modern websites. If you still want to achieve that unique <code>marquee</code> aesthetic, this article is here to help. First, we learn about the history of the marquee element and look at the pros and cons. Then <a href="https://htmhell.dev/adventcalendar/2022/15/#the-alternative">we code an alternative</a> with CSS and JavaScript.</p>
<h2 id="the-marquee-element">The Marquee element</h2>
<p>The <code>marquee</code> element has never been a standard HTML element and is even classified as a non-conforming feature in HTML5. This means it is entirely obsolete and must not be used, according to the <a href="https://www.w3.org/Consortium/">World Wide Web Consortium (W3C)</a>. Microsoft introduced the <code>marquee</code> element in Internet Explorer version 2.0, which was launched in 1995. Since then, it was quickly integrated into other browsers, although it never made it to the HTML specification.</p>
<h3 id="but-what-does-it-do?">But what does it do?</h3>
<p>When text or an image is placed inside a <code>marquee</code> element, it moves across the page infinitely, per default horizontally, from right to left.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- This scrolling text goes from right to left --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>marquee</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Newsflash: JLo and Ben Affleck got married!</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>marquee</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="Demo of a standard marquee">
<marquee>NEWSFLASH: JLo and Ben Affleck got married!</marquee>
</div>
<p>With the help of attributes, the content within the element can also scroll right, up or down. You can even control what happens when the corner of the screen is reached, how fast the animation is or if it has is a delay.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Using an attribute to change the scroll direction --></span></span><br /><span class="highlight-line"><span class="token comment"><!-- This scrolling text goes from top to bottom. The "scrollamount" attribute controls the speed. --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>marquee</span> <span class="token attr-name">direction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>down<span class="token punctuation">"</span></span> <span class="token attr-name">scrollamount</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Be prepared for low-rise jeans, Y2K is back.</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>marquee</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="Demo of a marquee scrolling top to bottom">
<marquee direction="down" scrollamount="1">Be prepared for low-rise jeans, Y2K is back.</marquee>
</div>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Using an attribute to change the scroll behaviour --></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>marquee</span><br /><span class="highlight-line"> <span class="token attr-name">direction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>up<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">behavior</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>250<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span></span><br /> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">border</span><span class="token punctuation">:</span>solid</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>marquee</span> <span class="token attr-name">behavior</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- This image bounces from the edges of the screen --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>40<span class="token punctuation">"</span></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>https://www.freeiconspng.com/uploads/dvd-logo-png-33.png<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>marquee</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>marquee</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="Demo of a marquee with a picture and different scroll behavior">
<marquee direction="up" behavior="alternate" width="250" height="200" style="border:solid">
<marquee behavior="alternate">
<!-- This image bounces from the edges of the screen -->
<img width="85" src="https://htmhell.dev/images/advent2022/14/dvd-logo-png-19280.png" />
</marquee>
</marquee>
</div>
<h3 id="the-popularity-of-marquee">The popularity of <code>marquee</code></h3>
<p>Although the marquee element was never accepted as a standard, people loved using it on their websites. It could be easily implemented and offered a bit of fun, way before CSS animations were a thing. <code>Marquee</code> was often used for under-construction pages or websites with headlines. Text or images moving across the screen served as a successful attention grabber.</p>
<p>You may be asking yourself why the <code>marquee</code> element gets a wave of attention now, in 2022. As the Y2K trend has a revival within fashion, brands also want to bring retro vibes and aesthetics to their websites. What better markets ultra-low-rise jeans than a website with blinking, colourful images and moving text? Well, you probably have guessed it: a website that uses modern user experience and accessibility practices to be enjoyable for everyone. Moving content grabs your attention, but it is also very distracting. Surfing the web should be a pleasant experience for every individual.</p>
<h3 id="some-reasons-against-marquee">Some reasons against <code>marquee</code></h3>
<p>Although moving text at high speed over a screen may sound compelling to try out the <code>marquee</code> element in your next web project, you really shouldn't. Here are some reasons for you if you are not convinced yet:</p>
<h4>1. Officially deprecated</h4>
<p>The usage of the <code>marquee</code> element is no longer recommended. It is officially marked as deprecated, meaning modern browsers might still support it for compatibility purposes. However, the element could be removed anytime, and newer or better alternatives are available. So, it should definitely not be used anymore.</p>
<h4>2. Not accessible</h4>
<p>The <code>marquee</code> element was launched without ever being tested properly, specifically regarding accessibility. This resulted in issues for people with visual impairments and cognitive disabilities. In addition, the element doesn't clearly define what its content is. It can be just text, an image, a link or something else. When assistive devices like screen readers don't know how to announce a website's content, their users might miss it or don't understand its purpose.</p>
<p>Automatically scrolling text can be unreadable by some people and severely distracting. Especially users with attention deficits have a hard time concentrating on other parts of the website when some content is moving or blinking. Some users also experience nausea from animated content. Such content can also cause problems for people using assistive technology to surf the web. These problems occur regardless of scrolling speed.</p>
<p>The latest version of the <a href="https://www.w3.org/TR/WCAG22/">Web Content Accessibility Guidelines (WCAG 2.2)</a> clearly presents recommendations to avoid the distraction of users during their interaction with a website. The <a href="https://www.w3.org/TR/WCAG22/#pause-stop-hide">success criterion 2.2.2</a> states that moving, blinking and scrolling information needs a mechanism to be paused or stopped. This rule applies if the animation:</p>
<ol>
<li>starts automatically</li>
<li>lasts longer than five seconds</li>
<li>is presented parallel with other content</li>
</ol>
<p>This requirement is Level A, meaning it is the minimum level of accessibility. Many users with disabilities will find it very difficult or impossible to access information. All websites should at least satisfy the minimum accessibility criteria.</p>
<h4>3. Usability problems</h4>
<p>The human eye is attracted to movement. As the content within the <code>marquee</code> element is constantly moving, it can be distracting for anybody, not just people with disabilities. Therefore using the marquee element results not only in accessibility but also in usability problems on a website.</p>
<p>When text inside a <code>marquee</code> element contains a link, additional problems occur. Clicking on a moving link is more complicated than clicking on static text. Depending on the speed and length of the scrolling text, some users may be unable to do so.</p>
<p>Users can also have difficulties printing a website if its content is not always fully visible. When multiple attempts are needed to complete the printing task, it makes it very inefficient, if not impossible.</p>
<h2 id="the-alternative">The Alternative</h2>
<p>If you still want to get the "marquee aesthetic", there is a way without using the element. CSS <a href="https://drafts.csswg.org/css-animations/">animations</a> are a more suitable method. Creating the scrolling effect with CSS needs a bit more code to implement than using a single line of HTML. However, CSS is still the recommended practice as it is used to style content and create presentational effects, whereas HTML is not. In addition, CSS can be used to create more powerful and advanced animations. So let's get right into it.</p>
<h3 id="replacing-the-marquee-element">Replacing the <code>marquee</code> element</h3>
<p>As a first step, we choose a standard HTML element instead of the <code>marquee</code> element. Depending on the type of content we want to animate, a different HTML element will be appropriate.</p>
<p>For the first example, we want to animate text. Therefore, we use the <code>p</code> element. We wrap this element inside a <code>div</code> container and add a class called <code>marquee-alternative</code>. This class name is arbitrary and was just added to style and animate our fake marquee with CSS later.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>marquee-alternative<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Newsflash: JLo and Ben Affleck got married!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="styling-the-fake-marquee-element">Styling the fake marquee element</h3>
<p>The classic HTML <code>marquee</code> element typically moves across the screen's whole width. To recreate this original effect for our fake marquee element, we first set the <code>overflow</code> to <code>hidden</code> on the <code>div</code> container, which has the class <code>marquee-alternative</code>. This makes sure that no unwanted vertical scrolling is possible. We also assure the container has a <code>width</code> of <code>100%</code>.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.marquee-alternative</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The <code>p</code> element inside the <code>marquee-alternative</code> container needs some basic styling as well. To remove all unwanted default spacing of the <code>p</code> element, we set the <code>margin</code> to <code>0</code>. We also set the <code>width</code> to <code>fit-content</code>. This assures that the <code>p</code> element has the length of its content. This is important as we want the text of our fake marquee element to loop through without any delay. If the <code>p</code> element had the whole width of the screen (100%), there would be a few seconds where our marquee text is not visible after it left the screen.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.right-to-left p</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> fit-content<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="adding-movement-with-css">Adding movement with CSS</h3>
<p>We can use CSS animations to move our fake marquee element over the screen. To create an animation, we use the<code>@keyframes</code> directive. Our custom animation called <code>marquee-right-to-left</code> starts at <code>100vw</code> (far right) and ends at <code>-100%</code> (far left). This means that our fake marquee begins outside the screen on the right side and moves to the left, ending outside the screen as well.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token atrule"><span class="token rule">@keyframes</span> marquee-right-to-left</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token selector">from</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>100vw<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token selector">to</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-100%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>You may wonder why we use <code>100vw</code> as the start value and not <code>100%</code>. This is because we previously set the width of the <code>p</code> element to fit its content. If we used <code>100%</code> as a start value, the animation would not start outside of the screen but somewhere else, depending on the content length.</p>
<p><strong>Note:</strong> If you implement your fake marquee element and it is not supposed to move across the whole screen width, you must adapt the <code>100vw</code> value to your use case.</p>
<p>To start our keyframe animation, we add some animation properties to our <code>p</code> element. We refer to our <code>marquee-right-to-left</code> keyframe by setting the <code>animation-name</code>. We also specify an <code>animation-duration</code> of 20 seconds, <code>linear</code> movement and <code>infinite</code> repetitions. Depending on your desired marquee aesthetic, you can adapt these values to change the animation style according to your needs.</p>
<p>The shortened version of adding our CSS animation would be <code>animation: marquee-right-to-left 20s linear infinite;</code> instead of writing the 4 separate properties.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.marquee-alternative p</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> fit-content<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">animation-name</span><span class="token punctuation">:</span> marquee-right-to-left<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">animation-duration</span><span class="token punctuation">:</span> 20s<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">animation-iteration-count</span><span class="token punctuation">:</span> infinite<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> linear<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<div data-demo="Demo of our fake marquee">
<div class="advent2022 marquee-alternative">
<p>Newsflash: JLo and Ben Affleck got married!</p>
</div>
</div>
<p>Now our fake marquee element should work, just like the old HTML classic. <strong>But wait, we are not finished yet!</strong></p>
<h3 id="dont-forget-accessibility">Donāt forget accessibility</h3>
<p>Some measures can be taken to make our fake marquee element more accessible and user-friendly.</p>
<h4>Reduce motion</h4>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion"><code>prefers-reduced-motion</code> media query</a> can be used to stop all animations for users that don't want them. As it is hard to decide when an animation is slow enough for people with disabilities, the best practice is to pause them entirely. In our case, we use the <code>prefers-reduced-motion</code> media query to only enable the animation if a user has no other preferences set.</p>
<p>We remove the <code>animation</code> property from the <code>.marquee-alternative p</code> nested class above and wrap it inside the media query.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.marquee-alternative p</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> fit-content<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">/* animation got removed here */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment">/* Only shows animation for users that have set no motion preference */</span></span><br /><span class="highlight-line"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-reduced-motion</span><span class="token punctuation">:</span> no-preference<span class="token punctuation">)</span></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* animation is added here */</span></span><br /><span class="highlight-line"> <span class="token selector">.marquee-alternative p</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">animation</span><span class="token punctuation">:</span> marquee-right-to-left 20s linear infinite<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h4>Pause the animation</h4>
<p>There are two different ways to pause the <code>marquee</code> animation. We can add a pause/play <code>button</code> or stop the animation on <code>hover</code>. Adding a <code>button</code> would be a cleaner and more accessible option. However, some projects may not allow adding an extra <code>button</code>. That is when we use the <code>hover</code> alternative with the help of a media query.</p>
<h5>Pause the animation on hover and on focus</h5>
<p>To ease the reading act and fight the issue of chasing moving text, we pause the animation when a user hovers over the marquee container (the <code>div</code>) or sets the focus on it. This is done by setting the <code>animation-play-state</code> to <code>paused</code> for the <code>p</code> element, on <code>hover</code> and <code>focus</code> of the <code>div</code>.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token comment">/* Pause animation on hover and on focus */</span></span><br /><span class="token selector">.marquee-alternative:hover p,<br />.marquee-alternative:focus p</span><span class="token punctuation">{</span><br /><span class="highlight-line"> <span class="token property">animation-play-state</span><span class="token punctuation">:</span> paused<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<div data-demo="Demo of our fake marquee that pauses on hover and focus">
<div class="advent2022 marquee-alternative-2" tabindex="0">
<p>Newsflash: JLo and Ben Affleck got married!</p>
</div>
</div>
<h5>Add a pause/play button</h5>
<p>To comply with the previously named WCAG criterion 2.2.2, we add a play and pause button for the animation. Not every user has set their motion preferences to <code>reduce</code>. We still want to allow everybody to pause the animation if it is perceived as too distracting.</p>
<p><strong>Note:</strong> When you implement the pause/play button, remove the <code>animation-play-state: pause</code> from the previous step. Otherwise, it would confuse users why the text is paused although they haven't pressed the button yet.</p>
<p>As a first step, we wrap our existing <code>marquee-alternative</code> container into another <code>div</code> and assign it the class <code>marquee-container</code>. We then add a <code>button</code> element to start and stop the animation. It has the class <code>btn</code> and the <code>aria-pressed</code> state set to <code>false</code>. The <code>p</code> element gets the class <code>marquee-content</code> assigned to it. We also add the <code>animation-play-state</code> style to the <code>p</code> element to be able to change it later on with JavaScript.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>marquee-container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>marquee-alternative<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>marquee-content<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">animation-play-state</span><span class="token punctuation">:</span> running<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Newsflash: JLo and Ben Affleck got married!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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>btn<span class="token punctuation">"</span></span> <span class="token attr-name">aria-pressed</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>Pause<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now we add a little bit of styling to the new HTML elements. This CSS code is added in addition to the already existing styling from before.</p>
<p>The <code>.marquee-container</code> gets a <code>border</code> and <code>flex</code> position. All elements inside the container are aligned in its centre. The <code>.marquee-content</code>, which is our text, gets some styling as well.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.marquee-container</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 3px solid #000<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.marquee-content</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> fit-content<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>The <code>button</code> gets a basic styling by changing the <code>background-color</code>, <code>border</code>, <code>box-shadow</code>, <code>cursor</code> and <code>padding</code>. Make sure to add enough <code>padding</code> that the <code>button</code> has a minimum size of 48x48 px. This is the minimum click area a <code>button</code> should have to be accessible. <code>Hover</code> and <code>focus</code> styles are added to have a more apparent visual change when interacting with the button. We also change the visual appearance of the <code>button</code> when it gets pressed by styling it when <code>aria-pressed</code> is set to <code>true</code>.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.btn</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border-left</span><span class="token punctuation">:</span> 3px solid #000<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 1.1rem 1rem<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0.5rem 0.5rem -0.125rem #0005<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="token selector">.btn:hover,<br /><span class="highlight-line">.btn:focus-visible,</span><br />.btn[aria-pressed="true"]:hover</span> <span class="token punctuation">{</span><br /><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.btn[aria-pressed="true"]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0.5rem 0.5rem -0.125rem inset #0005<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>For the JavaScript part of this implementation, we first query the <code>marquee-content</code> (<code>p</code> element) and the <code>button</code> with the help of <code>document.querySelector</code> and save them into variables. This is done to make the code more readable.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="highlight-line"><span class="token keyword">let</span> marqueeText <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">".marquee-content"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token keyword">let</span> button <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">".btn"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>We then add an event listener for the <code>click</code> event of the <code>button</code>. When the <code>button</code> is clicked, we check if the animation is currently running by looking at the <code>animationPlayState</code>. If it is <code>running</code>, we stop the animation by setting the state to <code>paused</code>. We also change the <code>button.innerText</code> and set <code>ariaPressed</code> to <code>true</code>. As the <code>button</code> serves as a pause and play button, we also add the functionality of enabling the animation again after it was stopped. This is done by setting the <code>animationPlayState</code> back to <code>running</code> and changing the <code>button.innerText</code> and <code>ariaPressed</code> state again.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="highlight-line">button<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</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></span><br /><span class="highlight-line"> <span class="token keyword">const</span> isRunning <span class="token operator">=</span> marqueeText<span class="token punctuation">.</span>style<span class="token punctuation">.</span>animationPlayState <span class="token operator">==</span> <span class="token string">"running"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token comment">// change behaviour of animation and button</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>isRunning<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// stop the animation</span></span><br /><span class="highlight-line"> marqueeText<span class="token punctuation">.</span>style<span class="token punctuation">.</span>animationPlayState <span class="token operator">=</span> <span class="token string">"paused"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">// change button text</span></span><br /><span class="highlight-line"> button<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token string">"Play"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> button<span class="token punctuation">.</span>ariaPressed <span class="token operator">=</span> <span class="token string">"true"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// start the animation</span></span><br /><span class="highlight-line"> marqueeText<span class="token punctuation">.</span>style<span class="token punctuation">.</span>animationPlayState <span class="token operator">=</span> <span class="token string">"running"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token comment">// change button text</span></span><br /><span class="highlight-line"> button<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token string">"Pause"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> button<span class="token punctuation">.</span>ariaPressed <span class="token operator">=</span> <span class="token string">"false"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<div data-demo="Demo of our fake marquee with a pause and play button">
<div class="advent2022 marquee-container-3">
<div class="marquee-alternative-3">
<p class="marquee-content-3" style="animation-play-state: running;">Newsflash: JLo and Ben Affleck got married!</p>
</div>
<button type="button" class="btn-marquee" aria-pressed="false">Pause</button>
</div>
</div>
<p>Your fake marquee element can now be stopped and activated again with the click of a button. This allows users to decide for themselves if they want to see the scrolling content on the screen or not.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Using the marquee aesthetic is a fun way of animating web content and grabbing a user's attention. But please be aware of the <code>marquee</code> element's impact on people. Even if the desired retro aesthetic is achieved without using the actual element but the presented alternative, the same named usability and accessibility concerns remain. So if you implement scrolling, animated content on your website, be conscious of its downsides and take measures to make the experience more accessible.</p>
Table Like It's 2023
2022-12-14T00:00:00Z
https://htmhell.dev/adventcalendar/2022/14/
<p>In this article:</p>
<ul>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#hello-website-builders!">Hello, Website Builders!</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#a-little-history">A little history</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#what-is-a-table?">What is a table?</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#who-benefits-from-tables?">Who benefits from tables?</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#what-does-a-table-look-like?">What does a table look like?</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#what-does-a-table-sound-and-feel-like?">What does a table sound and feel like?</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#(re)learning-tables-(1994-2022)">(Re)learning tables (1994 - 2022)</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#wcag-levels-unlocked">WCAG levels unlocked</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#when-tables-get-complicated">When tables get complicated</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#go-forth-and-make-good-tables">Go forth, and make good tables</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#learn-html-(including-tables)-for-free">Learn HTML (including tables) for free</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#testing-tools">Testing tools</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#special-thanks">Special thanks</a></li>
<li><a href="https://htmhell.dev/adventcalendar/2022/14/#about-amy-carney">About Amy</a></li>
</ul>
<h2 id="hello-website-builders!">Hello, Website Builders!</h2>
<p>Hey, you. Yes, you. Let's talk about tables on the web.</p>
<p>It's almost 2023. Yet tabular data is still being delivered in ways that aren't accessible. From a visually disabled person's perspective, it's horrifying.</p>
<p>Data tables aren't given semantic HTML to clarify its information relationships. Table markup is still used for page layouts, yet they're aren't flagged as such. The <a href="https://almanac.httparchive.org/en/2022/accessibility#tables">Web Alamanac</a> reported how infrequent <code>caption</code> (a table's title) and <code>role=presentation</code> (indicating layout-only) were used in 2022 to improve accessibility.</p>
<p>I'm embarrassed on behalf of my fellow developers, who should be more up-to-date with their craft and aware of current standards and best practices. My hope is to persuade you to make better choices as you build your website.</p>
<h2 id="a-little-history">A little history</h2>
<p><a href="http://www.barrypearson.co.uk/articles/layout_tables/history.htm">Table markup has been available since 1994</a>, despite not being an <a href="https://www.w3.org/TR/2018/SPSD-html32-20180315/#table">official HTML 3.2 recommendation</a> until 1997. If you were writing front-end code at that time, you probably did what a lot of us did: use <code>table</code> for layout AND tabular data. We didn't have many alternatives then. It didn't help that the specification stated that tables could be used for "layout purposes."</p>
<p>But that was then. We have better options now. HTML 4.01 gave us separate stylesheets. CSS iterations have given us multiple ways to layout pages, including:</p>
<ul>
<li>CSS grid,</li>
<li>flexbox,</li>
<li>multi-columns, and</li>
<li>positioning rules.</li>
</ul>
<p>Yet, 28 years later, the inaccessible tables I've seen (and remediated) have me scratching my head. Are today's developers learning from old knowledge delivered in tutorials and Stack Overflow answers? Despite reliable, easy-to-access resources, like <a href="https://developer.mozilla.org/en-US/">MDN (Mozilla Developer's Network)</a>, table and data abuse are still happening.</p>
<h2 id="what-is-a-table?">What is a table?</h2>
<p>To know when and how to use a table, you need to understand what a table is. A table is for tabular data.</p>
<p>WHATWG's (Web Hypertext Application Technology Working Group) <a href="https://html.spec.whatwg.org/multipage/tables.html#tables">HTML Living Standard states about tables</a>, "The table represents data with more than one dimension, in the form of a table."</p>
<p>If you're less familiar with the HTML Living Standard, you may recognize MDN. <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table">MDN describes an HTML table</a> as, "tabular data ā that is, information presented in a two-dimensional table comprised of rows and columns of cells containing data."</p>
<h2 id="who-benefits-from-tables?">Who benefits from tables?</h2>
<p>Ok, maybe you already <em>know</em> what a table is.</p>
<p>But do you know who benefits from proper table markup and styling? By "proper table markup", I mean the <code>table</code> element, not <code>div</code> or <code>img</code> elements.</p>
<p>Generally, people with visual and cognitive disabilities benefit.</p>
<p>Specifically, people who:</p>
<ul>
<li>use a screen reader,</li>
<li>magnify their screen,</li>
<li>use Windows High Contrast Mode,</li>
<li>use dark mode,</li>
<li>use custom stylesheets,</li>
<li>copy & paste,</li>
<li>import data into other programs, and</li>
<li>save pages as PDFs.</li>
</ul>
<p>To be honest, I'm hard-pressed to find anyone who doesn't benefit from thoughtful markup and styling of table data.</p>
<h2 id="what-does-a-table-look-like?">What does a table look like?</h2>
<p>By this point, we know a table looks like a 2D grid of rows and columns. Is there more to consider? Yes!</p>
<p>We want to help people make visual sense of a table. To be more inclusive, content creators, designers, and developers should evaluate their choices when it comes to:</p>
<ul>
<li>contrast between background color and foreground text,</li>
<li>font family, size, and spacing,</li>
<li>border visibility, color, and thickness,</li>
<li>color choices that can be overwritten by custom stylesheets and Windows High Contrast Mode, and</li>
<li>hover-activated changes that exclude keyboard and mobile device users.</li>
</ul>
<p>As someone visually disabled, who needs strong contrast and screen magnification, I need borders that stand out. When I magnify my screen, a solid line guides me along each row. As I rely on strong contrast between background, border, and text, I need borders and backgrounds to <em>not</em> be a light gray. Not to mention, if I use Windows High Contrast Mode that day, your background colors will disappear.</p>
<h2 id="what-does-a-table-sound-and-feel-like?">What does a table sound and feel like?</h2>
<p>But what about people who don't rely on visual cues? Specifically, people who rely on text-to-speech (screenreader) software to read content and convey the context of the content (headings, tables, images, etc.).</p>
<h3 id="screen-reader-semantics">Screen reader semantics</h3>
<p>A table is self-explanatory by conveying its child relationships between data points. Cells are associated with the column and row they sit in. HTML elements have this accessibility built-in.</p>
<p>I highly recommend you watch LƩonie Watson's demonstration "How screen readers navigate data tables", approximately 2 minutes to watch.</p>
<div class="video">
<iframe width="560" height="315" src="https://www.youtube.com/embed/X1KR4u94cho" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
<p>You should also read her <a href="https://tink.uk/how-screen-readers-navigate-data-tables/">accompanying blog post about how screen readers navigate data tables</a>.</p>
<h3 id="screen-reader-keyboard-shortcuts">Screen reader keyboard shortcuts</h3>
<p>Did you know screen readers allow users to navigate with keyboard shortcuts? They can jump from table to table and interact with columns, rows, and cells within a table.</p>
<p>WebAIM provides helpful instructions on how to get started to test with a screen reader:</p>
<ul>
<li><a href="https://webaim.org/articles/jaws/">Using JAWS to Evaluate Web Accessibility</a></li>
<li><a href="https://webaim.org/articles/nvda/">Using NVDA to Evaluate Web Accessibility</a></li>
<li><a href="https://webaim.org/articles/voiceover/">Using VoiceOver to Evaluate Web Accessibility</a>:</li>
</ul>
<p>If you need a better sense of which screen readers are used the most, checkout <a href="https://webaim.org/projects/screenreadersurvey9/#primary">WebAIM's latest Screen Reader User Survey</a>.</p>
<p>Don't forget about mobile screen reader users. They have access to touch gestures to quickly jump between tables and navigate within a table, too. But some mobile device users pair their phone or tablet with a keyboard, giving them access to keyboard commands.</p>
<h3 id="learn-more-about-the-screen-reader-users-experience">Learn more about the screen reader user's experience</h3>
<p>Feel intimidated by screen readers? Check out these resources to get an introduction to screen readers and how people use them:</p>
<ul>
<li><a href="https://webaim.org/techniques/screenreader/">Designing for Screen Reader Compatibility</a></li>
<li><a href="https://youtu.be/8Rn5pXCdZWU">W3C Web Accessibility Perspectives: Text to Speech (YouTube)</a></li>
</ul>
<p>Your markup choices can make or break a user's experience.</p>
<h2 id="(re)learning-tables-(1994-2022)">(Re)learning tables (1994 - 2022)</h2>
<p>This is my largest section, by far. If you feel you need to move forward, here is my TL;DR (too long, didn't read):</p>
<ul>
<li>It's <strong>OK</strong> to use <code>table</code> in 2023! Make that tabular data make sense to everyone.</li>
<li>It's <strong>not</strong> OK to arrange tabular data in <code>div</code>s and <code>span</code>s, or other unrelated markup.</li>
<li>It's <strong>not</strong> OK to use an <code>img</code> of a table, which is inaccessible for many folks.</li>
<li>Update your table model knowledge; some elements and attributes have changed in the past 28 years.</li>
</ul>
<p>If you're still with me, here we go.</p>
<p>As I said, a lot has changed since 1994. We have more HTML elements to construct more meaningful (context for content) webpages. Styles have broken free from the building blocks of HTML to create a separate layer of aesthetics.</p>
<h3 id="when-should-you-use-a-table?">When should you use a table?</h3>
<p>Maybe you're still torn about if you should use a table or not.</p>
<p>Here's a few questions you can ask yourself:</p>
<ul>
<li>Do I have tabular data (grid of items related to one another)?</li>
<li>Do I have an image (<code>img</code>) of a table?</li>
<li>Does my table element have a <code>role=presentation</code> attribute on it?</li>
<li>Does my table use obsolete attributes, like <code>border</code>, <code>cellspacing</code>, or <code>cellpadding</code> with a value of 0?</li>
</ul>
<p>If you said, "yes" to the first two items, then you probably should use <code>table</code>. If you said, "yes" to the last two items, you probably should use CSS to design your layout instead.</p>
<p>Speaking of CSS, today we have the advantage of separating architecture from aesthetics. Several HTML table attributes are now obsolete, to be replaced by CSS properties. Let's dive into the HTML building blocks, then bring up the decorative part again.</p>
<h3 id="how-to-markup-tables">How to markup tables</h3>
<p>Ready to make that table now?</p>
<p>Let the HTML Living Standard be your guide:</p>
<p>"Expected children [of the table model] include, in this order:</p>
<ol>
<li>optionally a <code>caption</code> element,</li>
<li>followed by zero or more <code>colgroup</code> elements,</li>
<li>followed optionally by a <code>thead</code> element,</li>
<li>followed by either zero or more <code>tbody</code> elements OR one or more <code>tr</code> elements,</li>
<li>followed optionally by a <code>tfoot</code> element, optionally intermixed with one or more script-supporting elements."</li>
</ol>
<p>What does that look like for those who write or generate HTML?</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span> <span class="token comment"><!-- required --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>caption</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>caption</span><span class="token punctuation">></span></span> <span class="token comment"><!-- optional, but ensure table has an accessible name --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>colgroup</span><span class="token punctuation">></span></span> <span class="token comment"><!-- 0 or more --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>col</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>col</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>col</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>col</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>col</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>col</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>colgroup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>thead</span><span class="token punctuation">></span></span> <span class="token comment"><!-- thead optional --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span> <span class="token comment"><!-- tr required --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span> <span class="token attr-name">scope</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><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>thead</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tbody</span><span class="token punctuation">></span></span> <span class="token comment"><!-- tbody optional, 0 or more --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span> <span class="token attr-name">scope</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><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tbody</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tfoot</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tfoot</span><span class="token punctuation">></span></span> <span class="token comment"><!-- tfoot optional --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span></code></pre>
<h3 id="a-note-about-table-model-elements">A note about table model elements</h3>
<p>If you started coding tables in the 1990s, you may feel that table markup was simpler then. Did you know <a href="https://www.w3.org/TR/html4/">HTML 4.01</a> introduced new elements for row and column groups to the table model?</p>
<p>If you haven't already, consider adding these elements (from the HTML Living Standard) to your toolbox:</p>
<ul>
<li><a href="https://html.spec.whatwg.org/#the-colgroup-element">colgroup</a></li>
<li><a href="https://html.spec.whatwg.org/#the-col-element">col</a></li>
<li><a href="https://html.spec.whatwg.org/#the-thead-element">thead</a></li>
<li><a href="https://html.spec.whatwg.org/#the-tbody-element">tbody</a></li>
<li><a href="https://html.spec.whatwg.org/#the-tfoot-element">tfoot</a></li>
</ul>
<p>The ability to group rows and columns are useful for granular styling or organization of data. They are not essential for accessibility at this time.</p>
<h3 id="a-note-on-deprecated-attributes">A note on deprecated attributes</h3>
<p>Even in 2022, I've seen old attributes hanging around that have been dumped in the obsolete bin. It's time for a markup refresh, if you're still using obsolete attributes.</p>
<table>
<caption>Obsolete (deprecated) table attributes</caption>
<thead>
<tr>
<th scope="col">Elements</th>
<th scope="col">Attributes</th>
</tr><tr>
</tr></thead>
<tbody>
<tr>
<td>table</td>
<td>align, bgcolor, border, cellspacing, cellpadding, frame, rules, summary, width</td>
</tr>
<tr>
<td>caption</td>
<td>align, valign</td>
</tr>
<tr>
<td>colgroup</td>
<td rowspan="4">align, bgcolor, char, charoff, valign</td>
</tr>
<tr>
<td>thead</td>
</tr>
<tr>
<td>tbody</td>
</tr>
<tr>
<td>tfoot</td>
</tr>
<tr>
<td>tr</td>
<td>align, bgcolor, char, charoff, valign</td>
</tr>
<tr>
<td>th</td>
<td rowspan="2">align, nowrap, valign, width, height</td>
</tr>
<tr>
<td>td</td>
</tr>
</tbody>
</table>
<p><a id="wcag"></a></p>
<h2 id="wcag-levels-unlocked">WCAG levels unlocked</h2>
<p>The appropriate use of the <code>table</code> element now welcomes everyone to your site and app. But you get something out of it, too! Your project has come further in achieving accessibility success and unlocking new levels of the Web Content Accessibility Guidelines (WCAG).</p>
<p>Here are some WCAG success criteria (SC) you've managed to tackle with your user-driven approach:</p>
<ul>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html">Info and Relationships (1.3.1, Level A)</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/meaningful-sequence.html">Meaningful Sequence (1.3.2, Level A)</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/parsing.html">Parsing (4.1.1, Level A)</a></li>
</ul>
<p>Depending on your content, design, table width, and approach, you <em>may</em> have even covered:</p>
<ul>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html">Resize Text (1.4.4, Level AA)</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/images-of-text.html">Images of Text (1.4.5, Level AA)</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html">Visual Presentation (1.4.8, Level AAA)</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/images-of-text-no-exception.html">Images of Text - No Exception (1.4.9, Level AAA)</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/reflow.html">Reflow (1.4.10, Level AA)</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html">Non-text Contrast (1.4.11, Level AA)</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/text-spacing.html">Text Spacing (1.4.12, Level AA)</a></li>
</ul>
<p>No guarantees on those "bonus" criteria. But they are worth learning and exploring how to apply in the context of your page and website.</p>
<p>Go, you!</p>
<h2 id="when-tables-get-complicated">When tables get complicated</h2>
<p>Sometimes you don't want to reach for <code>table</code> because it's complicated, for one reason or another:</p>
<ul>
<li>You were given a graphic of a table, rather than text content, and don't have an easy way to make it real text, despite the existence of <a href="https://www.lireo.com/extracttable/">ExtractTable, which converts data from images</a>.</li>
<li>You're using a framework like Rails or a CMS like WordPress that generates HTML, and you can't edit it or it's difficult to edit.</li>
<li>You struggle with CSS.</li>
<li>The default design is ugly and doesn't "fit" into your site's design.</li>
</ul>
<p>Maybe at this point, you've gotten past those issues, and you're really trying. But sometimes tables are just... hard. You've been asked to put form inputs in there. Your table needs to be dynamically sortable. Or you have loads of information, but it barely fits on a large viewport, and horizontally overflows on a small viewport.</p>
<p>It's OK to get frustrated. We've all encountered challenges when given a complex design idea. And some solutions are imperfect.</p>
<p>What I'm sharing in this article is meant to lead you in the right direction. Categorizing and building things is hard in real life.</p>
<p>You could learn from Adrian Roselli, who has tackled some of the hard issues:</p>
<ul>
<li><a href="https://adrianroselli.com/2020/02/block-links-cards-clickable-regions-etc.html#Update02">Block Link, Cards, Clickable Regions, Row, Etc. - March 31, 2020 update</a></li>
<li><a href="https://adrianroselli.com/2019/05/uniquely-labeling-fields-in-a-table.html">Uniquely Labeling Fields in a Table</a></li>
<li><a href="https://adrianroselli.com/2022/01/accessible-cart-tables.html">Accessible Cart Tables?</a></li>
<li><a href="https://adrianroselli.com/2019/09/table-with-expando-rows.html">Table with Expando Rows</a></li>
<li><a href="https://adrianroselli.com/2018/02/tables-css-display-properties-and-aria.html">Tables, CSS Display Properties, and ARIA</a></li>
<li><a href="https://adrianroselli.com/2020/11/under-engineered-responsive-tables.html">Under-Engineered Responsive Tables</a></li>
<li><a href="https://adrianroselli.com/2022/07/its-mid-2022-and-browsers-mostly-safari-still-break-accessibility-via-display-properties.html">Itās Mid-2022 and Browsers (Mostly Safari) Still Break Accessibility via Display Properties</a></li>
</ul>
<p>And there are others to learn from, too:</p>
<ul>
<li><a href="https://www.matuzo.at/blog/highlighting-columns/#table1-occurrences?ref=bestwebsite.gallery">Highlighting columns in HTML tables by Manuel Matuzovic</a></li>
<li><a href="https://www.scottohara.me/blog/2019/04/19/tabled-tables.html">Unintentionally tabling table semantics by Scott O'Hara</a></li>
<li><a href="https://inclusive-components.design/data-tables/">Data tables by Heydon Pickering</a></li>
</ul>
<h3 id="other-things-to-beware">Other things to beware</h3>
<p>You've done your research, you've created solutions, but sometimes you still run into problems. Keep these things in mind:</p>
<ul>
<li>Nested tables can break a screen reader user's experience. Try rearranging your table, or make multiple tables to keep them simple.</li>
<li>Tables with lots of columns are hard to wrangle for the different screen sizes out there. There is no perfect solution. Experiment with ways to help the data reflow and stack without losing its context and relationships.</li>
<li>Changing the CSS <code>display</code> value on the <code>table</code> element breaks the semantics of the table and breaks a screen reader user's experience.</li>
<li>In Safari 16, VoiceOver for Mac will read a <code>caption</code> as the table's name, but will not see a nested heading level in:
<ul>
<li>the Web Rotor headings list, and</li>
<li>the "next heading" keyboard shortcut.</li>
</ul>
</li>
<li>HTML generators and Markdown commonly exclude some table elements and attributes, like caption and scope.</li>
</ul>
<h2 id="go-forth-and-make-good-tables">Go forth, and make good tables</h2>
<p>Whew! I feel like I could jabber on and on about the importance of table markup.</p>
<p>However, here are the main things I want you to remember :</p>
<ul>
<li>Don't use <code>table</code> for layouts.</li>
<li>Avoid nesting tables and simplify data relationships, where possible.</li>
<li>Ensure each table has an accessible name, whether it be:
<ul>
<li><code>table > caption</code></li>
<li><code>table aria-label={Table name}</code></li>
<li><code>figure > figcaption + table</code>;</li>
<li><code>h{#} id={table-name}</code> + <code>table aria-labelledby={heading id}</code> (use with caution).</li>
</ul>
</li>
<li>This shouldn't have to be said, but don't ARIA overload a table.</li>
<li>Test your work with a screen reader or, even better, a proficient screen reader user.</li>
<li>Take a deep breath and try not to overthink it.</li>
</ul>
<p>I urge you in the coming year, use real tables for tabular data!</p>
<h2 id="learn-html-(including-tables)-for-free">Learn HTML (including tables) for free</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics">HTML basics on MDN</a></li>
<li><a href="https://www.30daysofhtml.com/">30 Days of HTML by Jen Kramer</a></li>
<li><a href="https://www.w3.org/WAI/tutorials/tables/">Tables Tutorial by W3C-WAI</a></li>
<li><a href="https://webaim.org/techniques/tables/">WebAIM's Creating Accessible Tables</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/quickref/?currentsidebar=%23col_customize&tags=tables">How to Meet WCAG Quick Reference: tables tag filter</a></li>
<li><a href="https://websitesetup.org/html-tutorial-beginners/">Beginner's Guide to Writing Good HTML by Bruce Lawson</a></li>
<li><a href="https://thegymnasium.com/courses/GYM/107/0/about">Modern Web Design by Aaron Gustafson (sign-up with Aquent Gymnasium)</a></li>
<li><a href="https://web.dev/learn/html/">Learn HTML by Estelle Weyl</a></li>
</ul>
<h2 id="testing-tools">Testing tools</h2>
<ul>
<li><a href="https://pauljadam.com/bookmarklets/tables.html">Paul J. Adam's Tables Bookmarklet for Accessibility Testing</a></li>
<li><a href="https://wave.webaim.org/">WebAIM's WAVE tool</a></li>
</ul>
<h2 id="special-thanks">Special thanks</h2>
<p>This write-up wouldn't be possible without the generous time and critical review from my friends and accessibility allies:</p>
<ul>
<li><a href="https://www.lireo.com/">Deborah Edwards-OƱoro</a></li>
<li><a href="https://incl.ca/">Nic Steenhout</a></li>
<li><a href="https://adrianroselli.com/">Adrian Roselli</a></li>
<li><a href="https://www.matuzo.at/">Manuel Matuzovic</a></li>
</ul>
One day we'll have a fully customisable select
2022-12-13T00:00:00Z
https://htmhell.dev/adventcalendar/2022/13/
<p>Today, I want to look at a proposed HTML feature that may end up replacing a lot of <code><div></code>s-based custom input components: <code><selectmenu></code>.</p>
<h2 id="css-is-awesome">CSS <em>is</em> awesome</h2>
<p>I realise this calendar is about HTML. And I'll get to that. But first, let me start with CSS. CSS is fantastic, because it has allowed us to decide what our websites look like for over 25 years. This wasn't a given. Before CSS, you could only define the structure of your pages and browsers would decide their appearance.</p>
<p>Fast-forward to today, CSS not only exists, it has become extremely versatile. We have grids, custom properties, transforms and much more, and it is all pretty compatible across different browsers. The basic principle of CSS stays: styles are hints. What websites look like is a combination of the preferences of users, browsers and web developers.</p>
<p>But maybe in your design system, your select has a very specific style. Shadows around the listbox, brand-specific whitespace settings⦠or your select contains more than just strings of text, like flags with country names or avatars with user names. The <code><select></code> element won't let you go that far with CSS. Enter <code><selectmenu></code>.</p>
<h2 id="the-parts-of-lessselectmenugreater">The parts of <code><selectmenu></code></h2>
<p>The <a href="https://open-ui.org/prototypes/selectmenu/"><code><selectmenu></code> element</a> is a new HTML element proposed by the <a href="https://open-ui.org/">Open UI Community Group</a>. It has not yet landed in the <a href="https://html.spec.whatwg.org/">HTML Specification</a>, but it is being prototyped in <a href="https://www.chromium.org/Home/">Chromium</a>. You can test it by turning the āExperimental Web Platform Featuresā flag on (go to about:flags and search for this flag).</p>
<p>When you use a <code><selectmenu></code>, you'll find it is comprised out of different parts:</p>
<p><img src="https://htmhell.dev/images/advent2022/13/parts.jpg" alt="rendering of a selectmenu with arrows pointing to its individual parts; the closed part is called button, the icon within it marker, the value within it selected-value and the expanding part listbox" /></p>
<ul>
<li><code><select></code> - the root element</li>
<li><code><button></code> - the button you click to open the select</li>
<li><code><selected-value></code> - the currently selected value</li>
<li><code><marker></code> - the arrow that is in the button</li>
<li><code><listbox></code> - the popover that opens when you click the button</li>
<li><code><option></code> - the options within the listbox (just like in regular <code><select></code>)</li>
<li><code><optgroup></code> - an optional way of grouping options (just like in regular <code><select></code>)</li>
</ul>
<p>From: <a href="https://open-ui.org/prototypes/selectmenu">Anatomy of the selectmenu</a></p>
<h2 id="styling-lessselectmenugreater">Styling <code><selectmenu></code></h2>
<h3 id="styling-parts">Styling parts</h3>
<p>In <a href="https://www.w3.org/TR/css-shadow-parts-1/">CSS Shadow Parts 1</a>, currently a Working Draft (eg <a href="https://www.w3.org/2021/Process-20211102/#rec-track">not yet a full-blown standard</a>), a new pseudo element is introduced to allow styling parts of shadow roots: <code>::part()</code>.</p>
<p>Wait, wasn't Shadow DOM something Web Components-specific? Yes. But how <code><selectmenu></code> is implemented in the browser, is kind of Web Component-like. You could see it as a Web Component that ships with the browser.</p>
<p>With <code>::part</code>, you can style the different parts as they are described above, for instance, to style the listbox, you would do:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">selectmenu::part(listbox)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 3px 3px 3px 3px hotpink<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Or maybe you want to change what the arrow looks like:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">selectmenu::part(marker)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 3px 3px 3px 3px hotpink<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>(The <code><marker></code> is not yet added to the selectmenu explainer, but it was <a href="https://github.com/openui/open-ui/issues/548#issuecomment-1262650219">confirmed in an Open UI meeting</a></p>
<h3 id="styling-states">Styling states</h3>
<p>You will also be able to style a selectmenu based on whether it is open or closed, using the <code>:open</code> pseudo class:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">selectmenu</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 2px solid black<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">selectmenu:open</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">border-color</span><span class="token punctuation">:</span> hotpink<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Or maybe you want a different marker when it is open:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">selectmenu::part(marker)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line">// regular</span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">selectmenu::part(marker):open</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line">// open</span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<img src="https://htmhell.dev/images/advent2022/13/openclosed.jpg" loading="lazy" alt="two selectboxes, the first is closed, the second is open. the closed one has selector selectmenu::part(marker):closed, the open one has selectmenu::part(marker):open" />
<p><em>Different icons on opened and closed states</em></p>
<p>There will also be a <code>:closed</code> pseudo class, to style the closed state. It would be equivalent to <code>:not(:open)</code>. The <code>:open</code> and <code>:closed</code> pseudos are defined in the <a href="https://www.w3.org/TR/selectors/#open-state">Element Display State section of Selectors, Level 4</a>.</p>
<h2 id="customising-beyond-just-styling">Customising beyond just styling</h2>
<p>You can also replace the parts shown above with random markup of your own. The parts shown above are exposed as āslotsā, meaning you can add your own tree structure in your markup and use that to replace a part.</p>
<p>For instance, this is how you would replace the icon with something else:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>selectmenu</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">slot</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>marker<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- your own selectmenu open icon goes here --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>selectmenu</span><span class="token punctuation">></span></span></span></code></pre>
<p>This uses whatever is inside this <code>div</code> instead of the default marker in the button to open a selectmenu.</p>
<p>In principle, <code><selectmenu></code> will allow you to do things like this with each of the parts, so that you could replace the button or the options. This gives developers a lot of power, and with that comes responsibility. So a word of warning: nesting the wrong kind of elements into parts can quickly lead to a āHTMHellā. For instance, what if you put more than just text inside of an <code>option</code>āsay, avatarsābut browsers and assistive technologies expect just text? Or what if you put a <code>button</code> in an <code>option</code>? Nested interactive elements like that risk breaking the experience for end users, in ways that the browser or assistive technologies can't magically fix, so beware.</p>
<h2 id="current-status">Current status</h2>
<p>Selectmenu is currently being āincubatedā at the Open UI Community Group and prototyped behind a flag in Chromium.</p>
<p>What about other browsers and browser engines? I'm not sure what will happen in Safari or Webkit, but folks from Firefox (Mozilla) join the Open UI calls. Quoting from the āThe Declarative Webā part of Mozilla's vision for the open web:</p>
<blockquote>[on the web, built-in controls are] often insufficiently styleable and have inconsistent internal structures across browsers, which makes it difficult to make them visually consistent with the rest of the Web page. We want to fill these gaps, and are pleased to see the OpenUI effort already making progress in this space.</blockquote>
<p>For <code><selectmenu></code> to become a reality, it would need to go into the standards and then get browser implementations. At this time, it's hard to say when that will be the case.</p>
<h2 id="summary">Summary</h2>
<p>So, in summary: <code><selectmenu></code> is a new proposal that would add a new control to the web platform that is like <code><select></code> by default, but can be styled and customised in pretty much any way you want. I'm particularly excited about the ability to use <code>::part()</code> to style parts, and slightly worried about the ability to replace parts completely as that may break accessibility if it is replaced with the wrong things.</p>
<p>You can play with <code><selectmenu></code> today in Microsoft Edge Canary or Google Chrome Canary and <a href="https://github.com/openui/open-ui/issues/new">file issues</a> against the Open UI repository.</p>
DOM Clobbering
2022-12-12T00:00:00Z
https://htmhell.dev/adventcalendar/2022/12/
<h3 id="motivation">Motivation</h3>
<p>When thinking of HTML-related security bugs, people often think of script injection attacks, which is also known as <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">Cross-Site Scripting</a> (XSS). If an attacker is able to submit, modify or store content on your web page, they might include evil JavaScript code to modify the page or steal user information like cookies.<br />
Most developers out there protect their websites against XSS by disallowing or controlling script execution.</p>
<p>However, this article is <strong>NOT</strong> about XSS.</p>
<p>This article is about an interesting attack that relies on HTML-only injections. The techniques here were <a href="http://www.thespanner.co.uk/2013/05/16/dom-clobbering/">first described by Gareth Heyes in 2013</a>, the topic gained additional attention due to a <a href="https://publications.cispa.saarland/3756/">recent research paper</a> and its accompanying website <a href="https://domclob.xyz/">https://domclob.xyz/</a> from Khodayari et al.</p>
<h3 id="background">Background</h3>
<p>Before we dive into the vulnerability class of DOM Clobbering, we need to establish some background.</p>
<p>Have you ever noticed that whenever you include a HTML element with an <code>id</code> attribute, it becomes available as a global name (and a name on the <code>window</code> object)? So, for example</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>freddy</span><span class="token punctuation">></span></span></span></code></pre>
<p>ā¦will lead to <code>window.freddy</code> becoming a reference to that form element.</p>
<p>This is a legacy inheritance from the very old days of HTML and also known as <em><a href="https://html.spec.whatwg.org/multipage/nav-history-apis.html#named-access-on-the-window-object">named property access</a></em> in the HTML spec. Studying the spec will inform us that the following elements will also appear on the <code>document</code> object: <code>embed</code>, <code>form</code>, <code>img</code>, and <code>object</code>.</p>
<p>Given that web browsers have to maintain backwards compatibility support for relatively old web pages, <em>named property access</em> is designed in such a way <a href="https://webidl.spec.whatwg.org/#legacy-platform-object-abstract-ops">that element references come before lookups of APIs</a> and other new attributes on the <code>window</code> and <code>document</code> object.</p>
<h3 id="dom-clobbering-attacks">DOM Clobbering Attacks</h3>
<p>In essence, this means that an attacker with the ability to inject HTML may mess with simple Web APIs - like the property <code>document.hidden</code>:</p>
<p>Imagine your website is trying to reduce or stop animations based on page visibility like so:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>document<span class="token punctuation">.</span>hidden<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token function">startAnimations</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>an injected HTML form element will be able to make this truthy, regardless of whether the page is really hidden:</p>
<p>With <code><form id=hidden></code> the value <code>document.hidden</code> would return a <code>HTMLFormElement</code> and therefore be considered truthy.</p>
<p>So, in essence, DOM Clobbering is an attack where injected HTML may confuse the existing JavaScript application and therefore change the application logic.</p>
<p>Another typical example of this vulnerability is where code is trying to fall back to a default config if some variable is set, like so:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">let</span> config <span class="token operator">=</span> defaultConfig <span class="token operator">||</span> <span class="token punctuation">{</span> <span class="token comment">/* some config here */</span><span class="token punctuation">}</span><span class="token punctuation">;</span></span></code></pre>
<p>If the <code>defaultConfig</code> name is clobbered, that other config is never going to be assigned correctly!</p>
<p>Let's look at a third, more complicated example: In this case, an attacker has injected two input elements with a <code>id</code> attribute of <code>childNodes</code> into an existing form <code>f</code> like here:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>f</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>childNodes</span><span class="token punctuation">></span></span>foo</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>childNodes</span><span class="token punctuation">></span></span>bar</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>text</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>hidden-from-js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>These injected input elements will overwrite the <code>form</code> element's property and all following JavaScript code will <strong>not</strong> be able to iterate over <code>f.childNodes</code>!</p>
<p>Compare the clobbered property with the actual children in this screenshot below:</p>
<p><img src="https://htmhell.dev/images/advent2022/6/image1.png" alt="" /></p>
<p>Do you see the difference in length?</p>
<p>An attack vector like this one could be used to fool or confuse the website's JavaScript code when iterating over attacker-controlled DOM trees.</p>
<p>Just imagine what else could be overridden!</p>
<p>How would your web page behave if seemingly innocent function calls like <code>document.getElementById()</code> can fail with "Uncaught TypeError: <code>document.getElementById</code> is not a function" because the API has also been clobbered</p>
<h3 id="prevention">Prevention</h3>
<p>The best way to prevent DOM Clobbering is to use a strong <a href="https://en.wikipedia.org/wiki/HTML_sanitization">Sanitizer</a> library. A sanitizer typically goes through user-supplied HTML and only leaves the harmless bits - removing scripts, event handlers and other injections - like DOM Clobbering.</p>
<p>The author of this article recommends <a href="https://github.com/cure53/DOMPurify/">DOMPurify</a>. By default, DOMPurify will remove all clobbering properties. Using the option <code>SANITIZE_NAMED_PROPS: true</code> will instead prefix user-supplied identifiers with <code>user-content-</code>.</p>
<p>However, if you want to live on the edge you might also want to look at the upcoming <a href="https://wicg.github.io/sanitizer-api/">Sanitizer API</a>. The specification for the Sanitizer API is still in development, but Chrome and Firefox are already shipping a prototype and the <a href="https://sanitizer-api.dev/">Sanitizer API Playground</a> has instructions on how to enable it.</p>
<p>In order to fix DOM Clobbering with the Sanitizer API, you need to configure the Sanitizer to disallow attributes of type <code>name</code> and <code>id</code>:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">const</span> mySanitizer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Sanitizer</span><span class="token punctuation">(</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> blockAttributes<span class="token operator">:</span> <span class="token punctuation">[</span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token string">'name'</span><span class="token operator">:</span> <span class="token string">'id'</span><span class="token punctuation">,</span> elements<span class="token operator">:</span> <span class="token string">'*'</span><span class="token punctuation">}</span><span class="token punctuation">,</span></span><br /><span class="highlight-line"> <span class="token punctuation">{</span><span class="token string">'name'</span><span class="token operator">:</span> <span class="token string">'name'</span><span class="token punctuation">,</span> elements<span class="token operator">:</span> <span class="token string">'*'</span><span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">]</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line">someElement<span class="token punctuation">.</span><span class="token function">setHTML</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> <span class="token punctuation">{</span>sanitizer<span class="token operator">:</span> mySanitizer<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>Good luck!</p>
There can be only one: Options for building āchoose oneā fields
2022-12-11T00:00:00Z
https://htmhell.dev/adventcalendar/2022/11/
<p>When it comes to building out forms, it sometimes seems like there are at once both too few field types and too many. This is especially true when it comes to having users choose an option from a pre-defined list, also known as āchoose oneā fields.</p>
<p>This article will take you on a three-stop tour of the most common ways to enable this kind of response in your forms, using just HTML. Iāll talk about some of the implications for CSS and JavaScript as well, but this is HTMHell after all, so my main focus is on the markup.</p>
<h2 id="selection-controls-(aka-drop-downs)">Selection controls (a.k.a., drop-downs)</h2>
<p>The <code><select></code> dropdown has been a mainstay of web forms since they first debuted in HTML 2. This relatively simple element allows for an author to define multiple choices for the user to choose from and the whole thing is neatly packaged in a compact control that can be manipulated via the mouse or keyboard or both.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Orange<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<p class="demo">
<label for="fruit">Whatās your favorite fruit?</label><br />
<select name="fruit" id="fruit">
<option>Apple</option>
<option>Orange</option>
</select>
</p>
<p>By default, the text value of the chosen <code><option></code> is the value of the field. Authors can override this behavior by setting a <code>value</code> on the <code><option></code> too:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>location<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Where are you based?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>location<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>location<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>US<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>United States<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>non-US<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Outside the United States<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<p class="demo">
<label for="location">Where are you based?</label><br />
<select name="location" id="location">
<option value="US">United States</option>
<option value="non-US">Outside the United States</option>
</select>
</p>
<p>By default, the first <code><option></code> in the list is used as the default value for the field, which is why you often see <code><select></code> elements begin with an empty value:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</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>-- Choose One --<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Orange<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<p class="demo">
<label for="fruit2">Whatās your favorite fruit?</label><br />
<select name="fruit" id="fruit2">
<option value="">-- Choose One --</option>
<option>Apple</option>
<option>Orange</option>
</select>
</p>
<p>Authors can specify a default value by setting the <code>selected</code> attribute on the appropriate <code><option></code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</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>-- Choose One --<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">selected</span><span class="token punctuation">></span></span>Orange<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<p class="demo">
<label for="fruit3">Whatās your favorite fruit?</label><br />
<select name="fruit" id="fruit3">
<option value="">-- Choose One --</option>
<option>Apple</option>
<option selected="">Orange</option>
</select>
</p>
<p><em>Note: If multiple <code><option></code> elements in the same <code><select></code> have a <code>selected</code> attribute applied to them, the last one in the source order wins.</em></p>
<p>Though itās seldom used, <code><select></code> fields also support grouping options using <code>optgroup</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>optgroup</span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Pome<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Pear<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>optgroup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>optgroup</span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Citrus<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Orange<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Tangerine<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>optgroup</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<p class="demo">
<label for="fruit4">Whatās your favorite fruit?</label><br />
<select name="fruit" id="fruit4">
<optgroup label="Pome">
<option>Apple</option>
<option>Pear</option>
</optgroup>
<optgroup label="Citrus">
<option>Orange</option>
<option>Tangerine</option>
</optgroup>
</select>
</p>
<p>Another benefit of <code><select></code> is that itās easy to validate on the client side. All you need to do is slap a <code>required</code> attribute on there and modern browsers will ensure a value is chosen before the form can be submitted. Donāt forget to use <code>aria-required="true"</code> to ensure that requirement is exposed to assistive technology as well.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<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>fruit<span class="token punctuation">"</span></span><br /> <span class="token attr-name">required</span> <span class="token attr-name">aria-required</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">value</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>-- Choose One --<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span> <span class="token attr-name">selected</span><span class="token punctuation">></span></span>Orange<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<p class="demo">
<label for="fruit5">Whatās your favorite fruit?</label><br />
<select name="fruit" id="fruit5" required="" aria-required="true">
<option value="">-- Choose One --</option>
<option>Apple</option>
<option selected="">Orange</option>
</select>
</p>
<p>One of the major benefits of <code><select></code> is that itās an extremely compact component, only displaying one choice at a time (provided you arenāt using the <code>multiple</code> variety that allows multi-selection). This can preserve a lot of screen real estate. That minimal footprint comes at a price, however, as it requires users to interact with the field to see all of the choices. Additionally, extremely long lists of choices will require users scroll the dropdown to see everything.</p>
<p>To speed up navigation in the list of choices, <code><select></code> elements do support keyboard-based movement. Users can use <kbd>up</kbd> and <kbd>down</kbd> to move around in the list. They can also jump around in the list by typing (a.k.a., "type ahead"). For example, if a user were filling out the field above and typed "o," the value would become "orange." If they opened the drop-down before typing, focus would move to "orange" but the value would not be set unless "orange" was chosen (using <kbd>enter</kbd> or <kbd>space</kbd>).</p>
<p>Itās worth noting that the matching algorithm for "type ahead" is limited. First, the string match needs to be with the start of the <code><option></code>ās display text. Second, <em>only</em> the display text is considered, meaning it will not match the <code>value</code> if it differs from what is shown to users.</p>
<p>Another limitation of the <code><select></code> is its style-ability (or lack thereof). In the past this was a <em>really</em> big deal because browsers rendered <code><select></code> drop-downs wildly differently and many of them were highly stylized to match the host OS.</p>
<table>
<caption>A selection of <code><select></code> styles across Windows XP and OS X, circa 2007</caption>
<thead>
<tr>
<th scope="col">OS</th>
<th scope="col">Browser</th>
<th scope="col"><code>select</code></th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="5">Windows XP</td>
<td>Firefox</td>
<td>
<img alt="Screenshot of a select in Firefox for Windows XP, showing the grey chunky old school drop-down" src="https://htmhell.dev/images/advent2022/10/select-xp-firefox.png" />
</td>
</tr>
<tr>
<td>IE 6/7 (XP)</td>
<td>
<img alt="Screenshot of a select in IE6 & IE7 for Windows XP in XP mode, showing the colorful, XP-themed drop-down" src="https://htmhell.dev/images/advent2022/10/select-xp-ie.png" />
</td>
</tr>
<tr>
<td>IE 6 (classic)</td>
<td>
<img alt="Screenshot of a select in IE6 for Windows XP in āclassicā mode, showing the grey chunky old school drop-down" src="https://htmhell.dev/images/advent2022/10/select-xp-ie6-classic.png" />
</td>
</tr>
<tr>
<td>IE 7 (classic)</td>
<td>
<img alt="Screenshot of a select in IE7 for Windows XP in classic mode, showing the grey chunky old school drop-down" src="https://htmhell.dev/images/advent2022/10/select-xp-ie7-classic.png" />
</td>
</tr>
<tr>
<td>Opera</td>
<td>
<img alt="Screenshot of a select in Opera for Windows XP, showing the grey chunky old school drop-down" src="https://htmhell.dev/images/advent2022/10/select-xp-ie7-classic.png" />
</td>
</tr>
<tr>
<td rowspan="5">OS X</td>
<td>Camino</td>
<td>
<img alt="Screenshot of a select in Camino for OS X, showing the 3D-stylized āAquaā theme drop-down" src="https://htmhell.dev/images/advent2022/10/select-osx-camino.png" />
</td>
</tr>
<tr>
<td>Firefox</td>
<td>
<img alt="Screenshot of a select in Firefox for OS X, showing the grey chunky old school drop-down" src="https://htmhell.dev/images/advent2022/10/select-osx-firefox.png" />
</td>
</tr>
<tr>
<td>IE 5</td>
<td>
<img alt="Screenshot of a select in IE5 for OS X, showing a muted grey toggle with up and down arrows (a wholly unique design)" src="https://htmhell.dev/images/advent2022/10/select-osx-ie5.png" />
</td>
</tr>
<tr>
<td>Opera</td>
<td>
<img alt="Screenshot of a select in Opera for OS X, showing the 3D-stylized āAquaā theme drop-down" src="https://htmhell.dev/images/advent2022/10/select-osx-opera.png" />
</td>
</tr>
<tr>
<td>Safari</td>
<td>
<img alt="Screenshot of a select in Safari for OS X, showing the 3D-stylized āAquaā theme drop-down" src="https://htmhell.dev/images/advent2022/10/select-osx-safari.png" />
</td>
</tr>
</tbody>
</table>
<p>Itās unsurprising that dozens upon dozens of <code><select></code> replacements arose during this period, most of which were highly inaccessible. I worked on one for agesāand <a href="https://www.google.com/books/edition/AdvancED_DOM_Scripting/Wmg6dkBJd50C?hl=en&gbpv=1&pg=PA507&printsec=frontcover">wrote a lengthy book chapter about the experience</a>. In the end, I managed to replicate most of the core functionality of a true <code><select></code> (including <code>optgroup</code> support, keyboard accessibility and more⦠all progressively enhanced) but it required a lot of extra code and was incredibly fragile (as most client-side JavaScript-dependent interfaces are). I never ended up deploying my <code><select></code> replacement because the extra download cost just didnāt seem worthwhile just to get a drop-down that looked the way I wanted it to.</p>
<p>Thankfully, browsers have largely toned down the design of their <code><select></code> drop-downs, making them blend more seamlessly into our websites. Weāve also been granted a bit more control when it comes to styling <code><select></code> elements, especially when we apply <code>appearance: none</code>.</p>
<p>The <code>appearance</code> property governs how an element is rendered. In the case of <code><select></code>, setting it to "none" removes the default UI bits that makes a <code><select></code> look like a <code><select></code>. If you go this route, you have a lot more control over the look and feel of the element, but you also need to add in UI affordances like the arrow indicator, which typically requires you to inject additional markup. Depending on how important it is for you to control the look of a <code><select></code>, you may find it worth doing. I generally donāt.</p>
<p>Itās worth noting that the Open UI team is actively working on <a href="https://open-ui.org/prototypes/selectmenu">a successor to <code><select></code> called <code>selectmenu</code></a>. It works largely the same as a <code><select></code>, but itās highly configurable. Its design can also be fully controlled via CSS. The <code>selectmenu</code> component is currently available as an experimental feature in Chromium-based browsers.</p>
<p>Though itās a bit of an edge case, the final challenge presented by the <code><select></code> elements is support for a user-defined value. While not impossible to achieve, it is a little more verbose, from a markup standpoint:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Orange<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Other<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit-other<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>If you chose "other," what <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>em</span><span class="token punctuation">></span></span>is<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>em</span><span class="token punctuation">></span></span> your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit-other<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p class="demo">
<label for="fruit6">Whatās your favorite fruit?</label><br />
<select name="fruit" id="fruit6">
<option>Apple</option>
<option>Orange</option>
<option>Other</option>
</select><br />
<label for="fruit-other">If you chose "other," what <em>is</em> your favorite fruit?</label><br />
<input id="fruit-other" name="fruit" />
</p>
<p>Using the above markup, you need to ensure your form processing logic overrides the initial "fruit" value with the user-supplied one. Most back-end languages do this automatically, but if youāre processing the data on the client side itās something to be aware of. Alternately, you could give the text <code><input></code> a unique <code>name</code> (e.g., "fruit_other") and check for "fruit" to equal "Other" before replacing that value with the "fruit_other" value.</p>
<p>Client-side validation of this construct is also a bit more involved. It requires that you use JavaScript to dynamically update the "other" fieldās <code>required</code> (and <code>aria-required</code>) status, based the user choice from the <code><select></code> dropdown.</p>
<p>There is a whole lot more we could explore when it comes to <code><select></code>, but this is a good introduction to the mechanics of the element.</p>
<table>
<caption>Summary: <code><select></code></caption>
<thead><tr>
<th scope="col">Pros</th>
<th scope="col">Cons</th>
</tr></thead>
<tbody><tr>
<td>
<ul>
<li>compact</li>
<li>value and display text can differ</li>
<li>supports value grouping</li>
</ul>
</td>
<td>
<ul>
<li>options are hidden</li>
<li>UI varies</li>
<li>CSS styling is limited</li>
<li>user-supplied values are challenging</li>
</ul>
</td>
</tr></tbody>
</table>
<h2 id="radio-controls">Radio controls</h2>
<p>As with <code><select></code>, radio controls (<code>input[type=radio]</code>) have been a part of HTML forms since they debuted. In order to be accessible, radio controls require a bit more markup as each <code><input></code> requires a <code><label></code>. Here is a minimalist example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Apple<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Apple</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Orange<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Orange</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="demo">
<fieldset>
<legend>Whatās your favorite fruit?</legend>
<label style="display: block">
<input type="radio" name="fruit_radio1" value="Apple" />
Apple
</label>
<label style="display: block">
<input type="radio" name="fruit_radio1" value="Orange" />
Orange
</label>
</fieldset>
</div>
<p>Hereās whatās going on in this code block:</p>
<ul>
<li>The <code>fieldset</code> is binding the whole construct together as a group</li>
<li>The <code>legend</code> provides the groupās label.</li>
<li>Each radio control (<code>input[type=radio]</code>) is ensconced in a <code><label></code> to ensure it reads properly for accessibility.</li>
</ul>
<p>I also generally like to wrap the choices within a list (typically a <code>ul</code>, unless the order matters) because I like how it looks when styles arenāt applied. You could also opt for a <code>div</code> or <code>p</code> or nothing at all (as I did in the above example). If you donāt use a wrapper, just make sure your <code><label></code> elements are set to <code>display: block</code> so the choices donāt run into one another.</p>
<p>You might be wondering why I chose to wrap the <code><label></code> elements around the radio <code><input></code> controls. This is something Iāve done for a long time because it creates an implicit <code><label></code> association with the <code><input></code>.<sup><a href="https://htmhell.dev/adventcalendar/2022/11/#note1">1</a></sup> It also allows me to write a really succinct selector to differentiate <code><label></code> elements used with radio controls (and checkboxes), which wrap the <code><input></code>, from those used for labeling other <code><input></code> types, <code><select></code>, and <code>textarea</code>, which donāt wrap the field.</p>
<p><sup id="note1">1</sup>: If you still have users on <em>really</em> old browsers (think IE7), you should double up with explicit association (using the <code>for</code> attribute as an <code>id</code> reference to the <code><input></code>) too as those older browsers require it.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">label</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-weight</span><span class="token punctuation">:</span> bold<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">label:has(input)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-weight</span><span class="token punctuation">:</span> normal<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>When folks talk about using <code><label></code>, they frequently focus on how crucial they are for the accessibility of your forms. Thatās absolutely true and you need them for that. What isnāt often discussed is that <code><label></code> elements have a hidden superpower: When you click or tap a <code><label></code> associated with a radio control, the <code><input></code> will be selected.<sup><a href="https://htmhell.dev/adventcalendar/2022/11/#note1">1</a></sup> That means using a paper label actually increases the hit target for the <code><input></code>, which is a usability win! In fact, proper labeling makes the age-old complaintāthat radio controls donāt have large enough hit targets to be mobile friendlyāentirely moot.</p>
<p><sup id="note2">2</sup>: When you click a <code><label></code> associated with another field type (e.g., <code><select></code>, <code>input[type=text]</code>), the field will receive focus. When you click a <code><label></code> associated with a checkbox <code><input></code>, it will toggle selection of the <code><input></code>.</p>
<p>Radio controls are perfect for when you want users to be able to more easily scan the available choices. Given that every choice is on display, however, they become less useful the more choices on offer. As a general rule, UX folks suggest <a href="https://www.justinmind.com/ui-design/radio-button-patterns-examples#:~:text=Some%20designers%2C%20like%20folks%20over%20at%20UX%20Movement%2C,space%2C%20while%20not%20asking%20too%20much%20from%20users">limiting radio controls to no more than 5 choices</a>, but there are exceptions to every rule. The most important thing to consider is whether users need to be able to read and evaluate the choices against one another. You should also do everything you can to <a href="https://www.aaron-gustafson.com/notebook/consider-how-your-forms-read/">ensure the choices are clear, concise, and make sense to your intended audience</a>.</p>
<p>Another benefit to using radio controls is that the <code><label></code> can be more than just text. There are limits, however. Since a <code><label></code> is interactive (see above), you canāt nest other interactive elements (e.g., <code>a</code>, <code>button</code>, <code>audio</code>, <code>video</code>) within one. Itās also recommended that you avoid putting headings (<code>h1</code>ā<code>h6</code>) inside a label as it can cause navigational issues for users of assistive technologies. You can, however, include other non-interactive <a href="https://developer.mozilla.org/docs/Web/Guide/HTML/Content_categories#phrasing_content">phrasing elements</a>, including images. That also means you can style the contents of a radio control <code><label></code> pretty much any way you like, provided its functionality is still obvious.</p>
<p>To establish a default <code>value</code> for the radio group, apply a <code>checked</code> attribute to the associated <code><input></code>. As with <code><option></code> lists, the last radio <code><input></code> in the group thatās <code>checked</code> will be the default value of the field.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Apple<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Apple</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Orange<span class="token punctuation">"</span></span> <br /> <span class="token attr-name">checked</span><span class="token punctuation">></span></span><br /><span class="highlight-line"> Orange</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="demo">
<fieldset>
<legend>Whatās your favorite fruit?</legend>
<label style="display: block">
<input type="radio" name="fruit_radio2" value="Apple" />
Apple
</label>
<label style="display: block">
<input type="radio" name="fruit_radio2" value="Orange" checked="" />
Orange
</label>
</fieldset>
</div>
<p>Radio controls can be designated as required too, using the same <code>required</code> attribute youād use in a <code><select></code>. You only need to add the attribute to one of the radio controls in the group to prevent the form from being submitted until the user has chosen one of the options. If you are working on a form where the choices change often, you might consider adding the <code>required</code> attribute to each of the <code><input></code> elements in order to ensure it doesnāt disappear if the one <code><input></code> in the group with that attribute gets removed for some reason. Thereās no penalty for having the attribute repeated on every <code><input></code> in the group.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Apple<span class="token punctuation">"</span></span><br /> <span class="token attr-name">required</span><span class="token punctuation">></span></span><br /><span class="highlight-line"> Apple</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Orange<span class="token punctuation">"</span></span><br /> <span class="token attr-name">required</span><span class="token punctuation">></span></span><br /><span class="highlight-line"> Orange</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="demo">
<fieldset>
<legend>Whatās your favorite fruit?</legend>
<label style="display: block">
<input type="radio" name="fruit_radio3" value="Apple" required="" />
Apple
</label>
<label style="display: block">
<input type="radio" name="fruit_radio3" value="Orange" required="" />
Orange
</label>
</fieldset>
</div>
<p>As with <code><select></code>, supporting user-defined values requires a bit more complexity:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Apple<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Apple</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Orange<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Orange</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Other<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Other</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit-other<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>If you chose "other," what <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>em</span><span class="token punctuation">></span></span>is<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>em</span><span class="token punctuation">></span></span> your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit-other<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span></span></code></pre>
<div data-demo="demo">
<fieldset>
<legend>Whatās your favorite fruit?</legend>
<label style="display: block">
<input type="radio" name="fruit_radio4" value="Apple" />
Apple
</label>
<label style="display: block">
<input type="radio" name="fruit_radio4" value="Orange" />
Orange
</label>
<label style="display: block">
<input type="radio" name="fruit_radio4" value="Other" />
Other
</label>
<br />
<p>
<label for="fruit-other">If you chose "other," what <em>is</em> your favorite fruit?</label>
<input id="fruit-other" name="fruit" />
</p>
</fieldset>
</div>
<p>All of the same considerations outlined in my discussion of this approach for <code><select></code> apply to user-supplied values in the context of radio controls as well. The JavaScript necessary to toggle <code>required</code> and <code>aria-required</code> on the text <code><input></code>, however, may be a little more complicated, depending on how you set up your event listeners.</p>
<p>While a little more complicated to author, radio controls remain a solid choice when creating a "choose one" type field.</p>
<table>
<caption>Summary: <code>input[type=radio]</code></caption>
<thead><tr>
<th scope="col">Pros</th>
<th scope="col">Cons</th>
</tr></thead>
<tbody><tr>
<td>
<ul>
<li>all choices are visible</li>
<li>choices can be more than just text</li>
</ul>
</td>
<td>
<ul>
<li>markup is more complicated</li>
<li>too many choices can be problematic</li>
<li>user-supplied values are challenging</li>
</ul>
</td>
</tr></tbody>
</table>
<h2 id="suggested-values">Suggested values</h2>
<p>The final option for "choose one" type fields is binding an <code><input></code> to a <code><datalist></code>. This option debuted as part of HTML5. The user experience is quite similar to the experience you get typing in your browserās address bar: As you type, the UI suggests values for you, based on what youāre typing.</p>
<p><img src="https://htmhell.dev/images/advent2022/10/datalist.gif" alt="Interacting with a datalist-backed input in Chrome Canary for macOS. When typing, suggestions are provided and users can pick one using the keyboard or mouse." /></p>
<p>The markup for this construct is pretty straightforward:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fruit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Whatās your favorite fruit?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit<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>fruit<span class="token punctuation">"</span></span> <span class="token attr-name">list</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fruit-options<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>datalist</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>fruit-options<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Apple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Orange<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>datalist</span><span class="token punctuation">></span></span></span></code></pre>
<p class="demo">
<label for="fruit">Whatās your favorite fruit?</label><br />
<input name="fruit" id="fruit" list="fruit-options" />
<datalist id="fruit-options">
<option>Apple</option>
<option>Orange</option>
</datalist>
</p>
<p>First, you create a <code><datalist></code> element, assign it an <code>id</code>, and fill it with <code><option></code> elements for the choices (making it look a lot like a <code><select></code>). Then you add a <code>list</code> attribute to the associated <code><input></code> and set its value to the <code>id</code> of the <code><datalist></code>. Itās worth noting that you can also reference a single <code><datalist></code> from multiple <code><input></code> fields, should you need to.</p>
<p><em>Note: Unlike in a <code><select></code>, the <code>selected</code> attribute does nothing when set on an <code><option></code> in a <code><datalist></code>. Use the <code>value</code> attribute on the <code><input></code> to provide a default value.</em></p>
<p>As with the <code><select></code>, <code>input[list]</code> is a very compact choice because itās just a single <code><input></code> field. Also like <code><select></code>, however, this approach doesnāt make a userās potential choices very obvious. In fact, you could argue it makes them even less obvious than a <code><select></code> does, on account of the predictive typing behavior. That said, it does borrow some UX goodies from <code><select></code> in that users can make a choice from the "picker" UI using their keyboard or mouse. They also have the option to just type out their response.</p>
<p>Thatās one of the real strengths of <code><datalist></code>: It naturally supports user-defined values over and above the <code><datalist></code> suggestions. Thatās a double-edged sword, of course, in that thereās no way to force a user to pick only from the choices you provide. You need to use a <code><select></code> if thatās a requirement.</p>
<p>Requiring a <code><datalist></code> field is just as straightforward as it is with the other field types. Add the <code>required</code> attribute (and <code>aria-required</code>) to the <code><input></code> and youāre good to go.</p>
<p>As I mentioned, <code><datalist></code> became a part of HTML much more recently than the other two options. Older browsers have no idea what to do when they encounter <code><datalist></code>, so they will ignore it. Thatās totally cool because you still end up with a usable text <code><input></code>. Perfect progressive enhancement!</p>
<p>The <code><datalist></code> approach is hands-down the best choice if you want to suggest potential responses, but also want to give users the option of providing a different value if they need to.</p>
<table>
<caption>Summary: <code><datalist></code></caption>
<thead><tr>
<th scope="col">Pros</th>
<th scope="col">Cons</th>
</tr></thead>
<tbody><tr>
<td>
<ul>
<li>compact</li>
<li>user-supplied values are easy</li>
<li>degrades to a standard <code><input></code></li>
<li>can fall back to <code><input></code></li>
</ul>
</td>
<td>
<ul>
<li>options are hidden</li>
<li>UI varies</li>
<li>not obvious that users can pick from pre-defined values</li>
<li>users doesnāt need to choose a pre-defined value</li>
</ul>
</td>
</tr></tbody>
</table>
<h2 id="make-your-choice">Make your choice</h2>
<p>Having seen these varying options, which one should you use? As with many things on the web, it depends on the context. If the choice is between a handful of items, a set of radio controls is likely the best option. Youād also want radio controls if the choices need to include something besides text. If the number of choices is substantial, a <code><select></code> or <code><datalist></code> might be the way to go. And if you need to keep things flexible, <code><datalist></code> is likely your best bet.</p>
<p>While it may be frustrating to have to evaluate so many options, itās also good to have the flexibility these different approaches enable.</p>
<hr />
<p><em>I used CodePen to assemble <a href="https://codepen.io/aarongustafson/pen/ExRXzMz">a minimally-styled demo of all of the fields discussed in this article</a>, if youād like to experiment with them yourself.</em></p>
Dear developer, your assumptions are wrong
2022-12-10T00:00:00Z
https://htmhell.dev/adventcalendar/2022/10/
<p>As developers, validation of user input is one of the first things we are taught.<br />
What we usually learn a lot later (or sometimes never), is to challenge our own biases and assumptions.</p>
<p>So, for example, we may think it would be a good idea to put some restrictions in an input field for a name:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>First name:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">minlength</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token attr-name">maxlength</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<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>name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>You might be surprised at discovering how many people in the world have names with just 1 or 2 letters (Read the article <a href="https://brucelawson.co.uk/2022/inclusive-name-inputs-because-not-everyone-is-called-chad-pancreas/">Inclusive name inputs ā because not everyone is called Chad Pancreas </a> by Bruce Lawson for examples), or maybe you can try to fit the full name of <a href="https://www.britannica.com/question/What-is-Picassos-full-name">Pablo Diego JosĆ© Francisco de Paula Juan Nepomuceno CrispĆn Crispiniano MarĆa de los Remedios de la SantĆsima Trinidad Ruiz Picasso</a> in your poor name field.<br />
Additionally, in many cultures of the world, no such thing as first name and last name separation exists, some people don't have any last name, and other similar cases.</p>
<p>But if you absolutely want to add some kind of validation, you can try with a <code>pattern</code> attribute:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>First name:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">pattern</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[a-zA-Z]{2,20}<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>name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>So... you're assuming names can only contain <em>A-Z</em> letters. What do we say then to Lee-Ann, Niccolò, NoĆ«mi, and Mohammed-Ibrahim? We say <a href="https://twitter.com/yournameisvalid">āyour name is invalid!ā</a>, of course.<br />
At least, let's try to be more kind in the error messages.</p>
<p><img src="https://pbs.twimg.com/media/FdV8wTpWQAEIIcG?format=png&name=small" alt="a screenshot showing error message saying: "please enter a valid name"" /></p>
<p>It's also not very nice when your phone auto-corrects your name with a wrong value. You could consider adding an <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete">autocomplete attribute</a> to the field, such as:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>First name:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">autocomplete</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">autocorrect</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>off<span class="token punctuation">"</span></span> <span class="token attr-name">spellcheck</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 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 punctuation">/></span></span></span></code></pre>
<p>There are a lot of things that we are not aware of, and thinking about all the possible edge cases and unusual scenarios can be really challenging. We should try to test with real users as much as possible or at least do some research on who they are / who they could be.</p>
<p>You can find more information about this validation problem on the <a href="https://www.w3.org/International/questions/qa-personal-names">W3C website</a>, together with possible solutions and suggested best practices.</p>
<p>Check out <a href="https://github.com/kdeldycke/awesome-falsehood">awesome-falsehood</a> to see more examples of wrong assumptions about names, phone numbers, addresses and so on.</p>
You don't need HTML!
2022-12-09T00:00:00Z
https://htmhell.dev/adventcalendar/2022/9/
<p>While browsing Mastodon late one night, I came across this excellent blog post called <a href="https://whitep4nth3r.com/blog/html-is-all-you-need-to-make-a-website/">HTML is all you need to make a website</a>. It describes a few websites which are pure HTML. No CSS and no JS.</p>
<p>And I thought⦠do you even need HTML to make a website?</p>
<p>A few hours later, I launched the <a href="https://no-ht.ml/">NO-HT.ML</a> website. Proving, once and for all, that you <em>don't</em> need HTML to make a beautiful<sup id="fnref-44154-ugly"><a href="https://htmhell.dev/adventcalendar/2022/9/#fn-44154-ugly" class="" title="[You ain't got no alibi](https://youtu.be/I96LR5vqqqQ?t=15).">1</a></sup> responsive<sup id="fnref-44154-resp"><a href="https://htmhell.dev/adventcalendar/2022/9/#fn-44154-resp" class="" title="In that it generates a violent response from web designers.">2</a></sup>, and useful<sup id="fnref-44154-use"><a href="https://htmhell.dev/adventcalendar/2022/9/#fn-44154-use" class="" title="I assume it will be used as part of my insanity defence.">3</a></sup> website which everyone will love<sup id="fnref-44154-hn"><a href="https://htmhell.dev/adventcalendar/2022/9/#fn-44154-hn" class="" title="[For some value of everyone](https://news.ycombinator.com/item?id=33645398).">4</a></sup>.</p>
<p>
<img src="https://htmhell.dev/images/advent2022/9/banner.jpg" alt="you don't need HTML writing in ascii characters." />
</p>
<p>Go <a href="https://no-ht.ml/">take a look and marvel at its magnificence</a> - then come back here to discuss its profound importance.</p>
<h2 id="how-it-works">How it works</h2>
<p>If you look at the source code, you'll see it uses a <em>tiny</em> scrap of (mostly) invalid markup to kick things off.</p>
<p><code><!doctype UNICODE></code> is a <em>fake</em> doctype. But it seems to be needed to convince some browsers into attempting to render the document.</p>
<p><code><meta charset="UTF-8"></code> is <em>real</em>. Without this, text encoding sniffing takes places and some browsers just displayed rubbish.</p>
<p><code><plaintext></code> is <em>also</em> a real HTML element. It was <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/plaintext">deprecated in HTML 2</a> back in the mid-1990s. What's particularly interesting is that the element <em>cannot</em> be closed. If you manage to find a website which lets you post HTML, that can be particularly fun!</p>
<p>From there, the page starts with some ASCII art. There are loads of text generators around. I remember when it used to take hours to hand-draw things like that.</p>
<p>Next is a bunch of Unicode abuse. There are <em>loads</em> of "letter-like" shapes. They're included for compatibility with rendering mathematical and scientific documents. There are also lots of punctuation marks - like bullets.</p>
<p>ASCII tables and flowcharts are pretty commonplace, although they sometimes go a bit wonky with non-fixed width characters.</p>
<p>And then, finally, the <a href="https://en.wikipedia.org/wiki/Arecibo_message">Arecibo message</a> using the <a href="https://unicodeplus.com/block/1F780">Geometric Shapes Extended Block</a>.</p>
<h2 id="is-this-useful">Is this useful?</h2>
<p>I've been reading "Reality is Broken" a book all about game design. In it, I found this quote:</p>
<blockquote>Playing a game is the voluntary attempt to overcome unnecessary obstacles<sup id="fnref-44154-cite"><a href="https://htmhell.dev/adventcalendar/2022/9/#fn-44154-cite" class="" title="Suits, The Grasshopper">5</a></sup>.</blockquote>
<p>It can be <em>fun</em> to give yourself arbitrary restrictions. In programming, there's a game called <a href="https://code.golf/">Code Golf</a> - where players compete to solve a coding challenge in the fewest number of characters.</p>
<p>That's what's going on here. People are playing a game - the rules of which are "Do normal thing X without using normal thing Y".</p>
<p>I encourage all of you to play games. Go use your imagination to build something in a ridiculously restricted way. What do the constraints do to the way you express yourself? What sense of satisfaction do you get from successfully creating something in an impossible manner? What will you learn from experimentation?</p>
<p>But, above all, have fun and show off your beautiful messes.</p>
<hr />
<div class="footnotes">
<ol>
<li id="fn-44154-ugly"><a href="https://youtu.be/I96LR5vqqqQ?t=15">You ain't got no alibi</a>. <a href="https://htmhell.dev/adventcalendar/2022/9/#fnref-44154-ugly" title="Return to main content.">ā©</a></li>
<li id="fn-44154-resp">In that it generates a violent response from web designers. <a href="https://htmhell.dev/adventcalendar/2022/9/#fnref-44154-resp" title="Return to main content.">ā©</a></li>
<li id="fn-44154-use">I assume it will be used as part of my insanity defence. <a href="https://htmhell.dev/adventcalendar/2022/9/#fnref-44154-use" title="Return to main content.">ā©</a></li>
<li id="fn-44154-hn"><a href="https://news.ycombinator.com/item?id=33645398">For some value of everyone</a>. <a href="https://htmhell.dev/adventcalendar/2022/9/#fnref-44154-hn" title="Return to main content.">ā©</a></li>
<li id="fn-44154-cite">Suits, The Grasshopper, 38. Katie Salen and Eric Zimmerman were among the first game researchers to outline these three characteristics as essential to a game, drawing on the work of Bernard Suits. Salen, Katie, and Eric Zimmerman. <a href="https://amzn.to/3VaFmxk">Rules of Play: Game Design Fundamentals</a> (Cambridge: MIT Press, 2004). <a href="https://htmhell.dev/adventcalendar/2022/9/#fnref-44154-cite" title="Return to main content.">ā©</a></li>
</ol>
</div>
Improving SEO without knowing where to start
2022-12-08T00:00:00Z
https://htmhell.dev/adventcalendar/2022/8/
<h2 id="summary">Summary</h2>
<ul>
<li><span id="s1"><a href="https://htmhell.dev/adventcalendar/2022/8/#part1">Introduction</a></span></li>
<li><span id="s2"><a href="https://htmhell.dev/adventcalendar/2022/8/#part2">What is SEO ?</a></span></li>
<li><span id="s3"><a href="https://htmhell.dev/adventcalendar/2022/8/#part3">Web quality with Opquast</a></span></li>
<li><span id="s4"><a href="https://htmhell.dev/adventcalendar/2022/8/#part4">SEO-related Opquast rules</a></span></li>
<li><span id="s5"><a href="https://htmhell.dev/adventcalendar/2022/8/#part5">Conclusion</a></span></li>
</ul>
<h2 id="introduction-ā"><span id="part1">Introduction <a title="back to the summary" href="https://htmhell.dev/adventcalendar/2022/8/#s1">ā</a></span></h2>
<p>Colleagues sometimes ask me: ā<em>Hey Alex, I would like to learn a bit about search engine optimisation (SEO) but I don't really know where to start. Do you have tips for me?ā</em>. Well, it's not surprising: like accessibility, sustainability and environment, or performance, SEO is a big topic.</p>
<p>I usually answer: ā<em>It's awesome that you want to dig into this. SEO is also a significant part of UX, and more globally of web quality assurance. Listen, I'm not a SEO expert but I know a solid start: these are the Opquast rules. If you apply them carefully, you will do better than most other websites.</em>ā</p>
<p>In this article, first I would like to describe shortly what SEO means, then present what Opquast is, and finally explain the benefits of some Opquast's SEO rules.</p>
<p>Disclaimer: Opquast is a society. I am neither an employee nor an affiliate.</p>
<h2 id="what-is-seo-?-ā"><span id="part2">What is SEO ? <a title="back to the summary" href="https://htmhell.dev/adventcalendar/2022/8/#s2">ā</a></span></h2>
<p><strong>SEO</strong> stands for <strong>S</strong>earch <strong>E</strong>ngine <strong>O</strong>ptimisation. It comprises <strong>improving the visibility</strong> of a page, a site, an application in search engine results pages (abbreviated <strong>SERP</strong>). Being positioned on the first page is obviously a good thing for the owner of a site, but it's also about helping search and indexing tools to extract content efficiently, helping users find your content, improving the visual and vocal reproduction of search results to users.</p>
<p>Many people say <em>āSEO is a marathon, not a sprintā</em>, and they're right. As Frederick OāBrien wrote in his article <a href="https://www.smashingmagazine.com/smashing-guide-search-engine-optimization/#playing-the-long-game">A Smashing Guide To The World Of Search Engine Optimization</a> on Smashing Magazine:</p>
<blockquote>Implementing best practice can produce immediate results, but long-term performance requires long-term maintenance.</blockquote>
<p>We could also establish that SEO is about getting as close as possible to the ā<em>requirements</em>ā of Google and its Page Rank calculation algorithm, but I suggest you don't bother too much with that, especially since the statements of SEO agencies, official communication from Google, and engineers at Google can sometimes contradict each other.</p>
<p>SEO is cool because <strong>it makes developers and designers care about web quality</strong> (I personally prefer this term rather than UX). Long ago, SEO strategies were mostly about accumulating backlinks (regardless of the quality) and putting important keywords everywhere.</p>
<p>Things have fortunately changed. Today, search engines will favor sites which care about users:</p>
<ul>
<li><strong>Content:</strong>
<ul>
<li>both in style (spelling, grammar, freshness of content, consistency between titles, content and keywords)</li>
<li>and substance (relevance, reliability, veracity, completeness)</li>
</ul>
</li>
<li><strong>Netlinking:</strong> quality backlinks</li>
<li><strong>Performance:</strong> <em>āHello Core Web Vitalsā</em><sup id="sup1"><a href="https://htmhell.dev/adventcalendar/2022/8/#ref1" aria-describedby="footnote-label">[1]</a></sup></li>
<li><strong>Responsiveness:</strong> today, more than half of search queries now come from mobile devices</li>
<li><strong>Semantics:</strong> a well-structured and intelligently designed site in HTML is easier to browse by an indexing robot, informations are extracted in a clearer way</li>
<li><strong>Accessiblity:</strong> (although it's not a direct ranking factor): always a good thing to remember, accessibility means that people with disabilities can perceive, understand, navigate, contribute and interact with the web<sup id="sup2"><a href="https://htmhell.dev/adventcalendar/2022/8/#ref2" aria-describedby="footnote-label">2</a></sup></li>
<li><strong>Security:</strong> HTTPS, protection from XSS, tracking, info leaks, etc.</li>
</ul>
<h2 id="web-quality-with-opquast-ā"><span id="part3">Web quality with Opquast <a title="back to the summary" href="https://htmhell.dev/adventcalendar/2022/8/#s3">ā</a></span></h2>
<p><a href="https://www.opquast.com/en/">Opquast</a> is a French pioneering company in web quality assurance, focused on ā<em>Making the web better</em>ā. Opquast is well-known in France for having created <a href="https://checklists.opquast.com/en/web-quality-assurance/">The Web Quality Assurance Checklist</a> (under open CC-BY-SA license, available in English, French and Spanish), a list of 240 rules to improve your sites and take better care of your users. Opquast ensures that each of these 240 rules is written in such a way that they are:</p>
<ul>
<li><strong>Transversal:</strong> they are understandable by everyone (developer, designer, dev ops, community manager, marketing manager, sales, web editor, project manager, etc.)</li>
<li><strong>Universal:</strong> they are the subject of consensus in consultation with a broad community of experts, agencies, companies and higher education</li>
<li><strong>Sustainable:</strong> they are made to last over time</li>
<li>easily <strong>Verifiable</strong></li>
<li><strong>Useful</strong> for users</li>
<li><strong>Realistic</strong></li>
</ul>
<p>These rules cover a wide range of topics (security, links, personal information, performance, navigation, images, forms, newsletters, structure, e-commerce, etc.).</p>
<p class="code-label">Some examples:</p>
<ul>
<li><a href="https://checklists.opquast.com/en/web-quality-assurance/the-period-of-validity-and-conditions-of-special-offers-and-promotions-are-indicated">Rule n° 39</a> - The period of validity and conditions of special offers and promotions are indicated</li>
<li><a href="https://checklists.opquast.com/en/web-quality-assurance/it-is-possible-to-unsubscribe-from-newsletters-from-the-website">Rule n° 171</a> - It is possible to unsubscribe from newsletters from the website</li>
<li><a href="https://checklists.opquast.com/en/web-quality-assurance/accounts-can-be-created-without-the-need-to-use-a-third-party-identification-system">Rule n° 16</a> - Account creation is possible without the need to use a third-party identification system.</li>
</ul>
<p>Among these 240 rules, 37 are SEO-related (you'll notice that some rules cover several topics at once) and I want to show you some of them.</p>
<h2 id="seo-related-opquast-rules-ā"><span id="part4">SEO-related Opquast rules <a title="back to the summary" href="https://htmhell.dev/adventcalendar/2022/8/#s4">ā</a></span></h2>
<h3 id="describe-information-about-the-page">Describe information about the page</h3>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/each-pages-source-code-contains-metadata-that-describe-the-content">Rule n° 3</a> - Each pageās source code contains metadata that describes the content.</blockquote>
<p>Thanks to <code>meta</code> tags, search engines can provide a short description of the page to users in search result pages (SERP). (Besides, it can be automatically displayed when you share a link on social networks).</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>information about the page here<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>If you omit it, you let search engines choose the description for you (and that's rarely a good thing).</p>
<h3 id="identify-the-site-and-its-content">Identify the site and its content</h3>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/each-page-provides-a-title-that-enables-one-to-identify-the-site">Rule n° 97</a> - Each page provides a title that enables one to identify the website.</blockquote>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/each-page-provides-a-title-that-enables-one-to-identify-its-content">Rule n° 98</a> - Each page provides a title that enables one to identify its content.</blockquote>
<p>Text in the <code>title</code> element is really important, it's used everywhere: tabs, bookmarks, favorites, homescreen phone, screen readers, share preview, social media. And in <abbr title="search engine results page">SERP</abbr> obviously.</p>
<p><img src="https://htmhell.dev/images/advent2022/16/bk-fail-98.jpg" alt="Screenshot of a search result showing the title and description from the Burger King "Customer Support" page. The title provided is just "Burger King". The description is "Discover our menu and order delivery or pick up from a Burger King near you. Burger King. Sign Up. Shopping cart preview $0.00 Cart total"" /></p>
<p>In the <code>title</code> element from the "Customer Support" page of the Burger King website, the text is just "Burger King". The site is identified but not the content (Notice how it fails rule n° 3).</p>
<h3 id="alternative-text">Alternative text</h3>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/each-decorative-image-has-an-appropriate-text-alternative">Rule n° 111</a> - Each decorative image has an appropriate text alternative.</blockquote>
<p>If an image doesn't provide any important information for the good understanding of the content, simply add <code>alt=""</code> to it. Otherwise, you let this sort thing happen:</p>
<p><img src="https://htmhell.dev/images/advent2022/16/casto-fail-111.jpg" alt="Screenshot of a search result showing the title and description from the Castorama "Find a store" page. The title provided is just "Trouver un magasin - Castorama". The description is "Tous les magasins Castorama. Auvergne-Rhone-Alpes. chevron right Bourg-en-bresse; chevron right Bourgoin-jallieu; chevron right Bron; chevron right ..."" /></p>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/each-information-carrying-image-has-an-appropriate-text-alternative">Rule n° 113</a> - Each information-carrying image has an appropriate text alternative.</blockquote>
<p>When an image contains textual and/or visual information useful to understanding the content, it must be described in the <code>alt</code> attribute. As you saw in the example of rule n° 97, alternative texts are also taken into account.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>f-incentiveBox__content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/brutX-logo-v2.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>b</span><span class="token punctuation">></span></span>1 MOIS OFFERT<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>b</span><span class="token punctuation">></span></span> puis 4,99ā¬/mois</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>This piece of HTML is from <a href="https://www.fnac.com/Ecouteurs-sans-fil-Sony-True-Wireless-WF1000XM4B-avec-reduction-de-bruit-Noir/a15967460/w-4">a product page on fnac.com</a>. The product is on sale with a one-month free offer at a video streaming platform (BrutX). But if you are an indexing robot (or a screen reader user), you wouldn't know because the <code>alt</code> attribute is missing.</p>
<h3 id="links">Links</h3>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/each-image-link-has-an-appropriate-text-alternative">Rule n° 112</a> - Each image link has an appropriate text alternative.</blockquote>
<p>Users hate unlabeled links. Indexing robots too. They will find this content irrelevant and hard to reference.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/actualite-sup_veto_formation_asv<span class="token punctuation">"</span></span> <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/imageDiplomeo032022.jpg<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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>This piece of HTML is from <a href="https://supveto.com/">supveto.com</a>. There is an <code>alt</code> attribute, but it's empty and it doesn't provide the link with an accessible name.</p>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/all-hyperlinks-internal-to-the-site-are-valid">Rule n° 147</a> - All links internal to the website are valid.</blockquote>
<p>I said indexing robots hate unlabeled links; they hate broken links even more. You might not be able to check external links regularly but regarding internal links, try to set up a routine (automated or not) to check their validity.</p>
<h3 id="a-bit-of-semantics">A bit of semantics</h3>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/each-pages-source-code-specifies-the-contents-main-language">Rule n° 125</a> - Each pageās source code indicates the contentās main language.</blockquote>
<p>Another easy way to help indexing robots crawl your content is to fill the <code>lang</code> attribute at the root of the document. Because the natural language of a page isn't always identifiable by tools as screen readers, translation tools, indexing robots, etc.</p>
<p class="code-label">Example for English:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p class="code-label">You can also indicate a temporary change of language inside content:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/german/content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Deutsch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> (German)</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>path/to/danish/content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>da<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Dansk<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> (Danish)</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ...</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<p>Available values are listed on <a href="https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry">iana.org</a>.</p>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/the-date-of-publication-or-update-of-the-contents-is-made-available-in-a-programmatic-form">Rule n° 224</a> - The date of publication or update of the contents is made available in a programmatic form.</blockquote>
<p>I always recommend showing a publication date on content pages. It allows users to put what they read into context (reading an article from 2007 is not the same as reading an article from 2022).</p>
<p>Indexing robots also appreciate having a date, even more when it's explicit. See some examples below:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line">Updated on <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2022-06-12<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>June 12<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line">Published on <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2022-09-27<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sep 27, 2022<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line">The event starts at <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20:00<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>8pm<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span>.</span></code></pre>
<p>More examples about this format on <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time#valid_datetime_values">developer.mozilla.org</a> and infos on <a href="https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#dates-and-times">html.spec.whatwg.org</a>.</p>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/each-pages-content-is-organized-according-to-a-hierarchical-structure-of-headings-and-sub-headings">Rule n° 227</a> - Each pageās content is organized according to a hierarchical structure of headings and sub-headings.</blockquote>
<p>Headings are a truly big topic. They are very helpful for users and assistive technologies to apprehend the structure of a page and navigate it. They are also useful for indexing tools because they help them understand what should be considered important. Thus:</p>
<ul>
<li>don't use too long headings.</li>
<li>don't skip levels (for example, an <code>h3</code> must not be followed by an <code>h5</code> or <code>h6</code>).</li>
<li>don't use <code>hx</code> only for visual purposes (use CSS instead)</li>
<li>don't use too many headings (if everything is important, nothing is).</li>
</ul>
<p><img src="https://htmhell.dev/images/advent2022/16/nike-fail-227.jpg" alt="Headings structure of nike.com/fr containing duplicate headings and holes in the structure" /></p>
<p>The structure would benefit from being clearer and more relevant here, especially the multiple headings āNike Sportwearā are really confusing.</p>
<h3 id="pdf">PDF</h3>
<p>Indexing robots crawl PDF documents as well. You can access a PDF document directly from a <abbr title="search engine results page">SERP</abbr>.</p>
<blockquote class="highlight"><a href="https://checklists.opquast.com/en/web-quality-assurance/the-text-of-internal-pdf-documents-can-be-selected">Rule n° 233</a> - The text of internal PDF documents can be selected.</blockquote>
<p>Not being selectable means that the content of the PDF cannot be crawled (in addition to not being able to be copied, searchable, clickable, translatable, vocalizable). Scanned document are a pain for users, for indexing robots too.</p>
<blockquote class="highlight"><a href="https://htmhell.dev/adventcalendar/2022/8/(https://checklists.opquast.com/en/web-quality-assurance/internal-pdf-documents-are-given-a-structure-based-on-headings">Rule n° 234</a> - Internal PDF documents are given a structure based on titles.</blockquote>
<p>This one is a variant of the rule 227. The same benefits apply here as well. Use the formatting tools (Heading 1, Heading 2, etc.) in your software to generate a structured PDF.</p>
<h2 id="conclusion-ā"><span id="part5">Conclusion <a title="back to the summary" href="https://htmhell.dev/adventcalendar/2022/8/#s5">ā</a></span></h2>
<p>That's it! There are other rules. I invite you to dig more into these (writing about every rule would have made this article way too long). Remember: your approach towards SEO has to be "<em>user first</em>" (search engines will reward you for that). Think this way about SEO, but also for accessibility, performance, privacy, responsive design, eco-conception. Imagine all these themes cohabiting and interacting in a virtuous circle.</p>
<h3 id="find-out-more">Find out more</h3>
<ul>
<li><a href="https://checklists.opquast.com/en/web-quality-assurance/">Opquast - The Web Quality Assurance Checklist</a></li>
<li><a href="https://developers.google.com/search/docs/fundamentals/get-started-developers">Google - Get started with Search: a developer's guide</a></li>
<li><a href="https://developers.google.com/search/docs/fundamentals/seo-starter-guide">Google - Search Engine Optimization (SEO) Starter Guide</a></li>
<li><a href="https://www.smashingmagazine.com/smashing-guide-search-engine-optimization/">Smashing Magazine - A Smashing Guide To The World Of Search Engine Optimization</a></li>
</ul>
<hr />
<h2 id="footnotes"><span id="footnote-label">Footnotes</span></h2>
<ol>
<li><span id="ref1">Web Vitals is an initiative by Google to provide unified guidance for quality signals that are essential to delivering a great user experience on the web. See <a href="https://web.dev/vitals/">web.dev/vitals</a>. <a title="back to content" href="https://htmhell.dev/adventcalendar/2022/8/#sup1">ā</a></span></li>
<li><span id="ref2">This definition is from the <em>Web Accessiblity Initiative</em>. More explanation at <a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/#what">w3.org/WAI</a>. <a title="back to content" href="https://htmhell.dev/adventcalendar/2022/8/#sup2">ā</a> </span></li>
</ol>
Meaningful labels using ARIA ā or not.
2022-12-07T00:00:00Z
https://htmhell.dev/adventcalendar/2022/7/
<p>If I had a dollar for every time I've had to tell someone to remove an <code>aria-label</code> from an interactive control that has actual visible text, I could have bought Twitter! As a former developer and current accessibility consultant, it is my sincere hope that by reading this article, developers unfamiliar with or unsure about this topic will have a better understanding of the differences between <code>aria-label</code>, <code>aria-labelledby</code>, and <code>aria-describedby</code>.</p>
<p>Semantic HTML elements convey meaning - role and state - to browsers and assistive technologies. Whenever you <strong>can</strong> use an appropriate native HTML element, you <strong>should</strong> do so. For example, using a <code><button></code>, instead of using a generic element such as a <code><div></code> and then adding ARIA roles and states and properties and JavaScript functions and CSS to make it look and behave like a button, is a much better way to code. In fact, the <a href="https://www.w3.org/TR/using-aria/#rule1">First Rule of ARIA</a> is: <strong>Use HTML</strong>. (<a href="https://twitter.com/stevefaulkner/status/1433774405797257222">Credit for that paraphrase</a>.)</p>
<h2 id="when-should-i-use-aria-to-label-something?">When should I use ARIA to label something?</h2>
<p>Using this <a href="https://adrianroselli.com/2020/01/my-priority-of-methods-for-labeling-a-control.html">priority of methods for labelling a control</a> list, first ask yourself, ādo I really need to use ARIA here?ā That is, can you add visible text to your element or programmatically associate existing text using native HTML semantics? There are multiple ways to do this; here are a few.</p>
<h3 id="html-lesslabelgreater-tag">HTML <code><label></code> tag</h3>
<p>Use the <code>for</code> attribute to tie a <code><label></code> to its input.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>fname<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First name:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fname<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>lname<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Last name:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lname<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span></code></pre>
<h3 id="html-lessfieldsetgreater-and-lesslegendgreater-tags">HTML <code><fieldset></code> and <code><legend></code> tags</h3>
<p>Use <code><fieldset></code> and <code><legend></code> tags to group and label radio button and checkbox sets.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>What is your favorite breakfast food?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>waffles<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fav_breakfast<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>waffles<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>waffles<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Waffles<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>pancakes<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fav_breakfast<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pancakes<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>pancakes<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Pancakes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>leftover_pizza<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fav_breakfast<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>leftover_pizza<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>pizza<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Leftover Pizza<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span> </span></code></pre>
<h3 id="html-alt-attribute-for-images">HTML <code>alt</code> attribute for images</h3>
<p>Use meaningful <code>alt</code> text for images, particularly when the image is the only content for a link or a button. The <code>alt</code> text in the next example is much more meaningful to a screen reader user for understanding the purpose of the link than simply describing the bags of money in the image would be.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/win-the-lottery<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>bags-of-money.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>How to win the lottery. Heaping bags of money to help prove the point.<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> </span></code></pre>
<p>ARIA should be used only to enhance HTML when necessary, not to "fix" bad HTML. That being said, there are times when it may be necessary to clarify or supplement labeling for various elements in order to make them more accessible for users of assistive technologies such as screen reading or voice control software.</p>
<p>There are some great tips on creating effective and user-friendly names in the tutorial <a href="https://hidde.blog/better-accessible-names/">better accessible names</a> that apply to all methods of labeling.</p>
<h2 id="should-i-use-aria-label-or-aria-labelledby?">Should I use <code>aria-label</code> or <code>aria-labelledby</code>?</h2>
<p><abbr title="too long, didn't read">TLDR</abbr>:</p>
<ul>
<li>If text for the label is available somewhere on the page, use <code>aria-labelledby</code>.</li>
<li>If text for the label doesn't exist on the page, use <code>aria-label</code>.</li>
</ul>
<p>The <code>aria-label</code> attribute accepts a string of text as its value. The <code>aria-labelledby</code> attribute takes the value of one or more <code>id</code> attributes. It's especially important to understand that <strong>both <code>aria-label</code> and <code>aria-labelledby</code> replace any existing label content</strong> for assistive technologies. Let's go over some examples.</p>
<h3 id="appropriate-uses-of-aria-label">Appropriate uses of <code>aria-label</code></h3>
<p>Interactive elements with informative icons or icon fonts that don't have accompanying text all need textual names to be accessible. Some examples are links to social media pages and icon-only buttons such as Print, Download, or Info. The <code>aria-label</code> attribute works great for these situations. Here, an icon is displayed using CSS for an otherwise empty link:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon-facebook social-icon<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>Facebook page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>You know that "X" button in the corner of a modal? Please, give it an <code>aria-label</code>! The following example will overwrite the visible text content of "X" and announce as "Close," which definitely makes more sense than hearing "X".</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>X<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>In some cases you could instead provide <code>alt</code> text that would create an appropriate button or link label (and get bonus points for following ARIA Rule #1!).</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>system-icon-circle-x.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>Close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span> </span></code></pre>
<p>When there are multiple navigation areas on a page, adding an <code>aria-label</code> to each can help a screen reader user differentiate between the regions and better understand where they are on the page. Don't include the word "navigation" as that is already announced via the <code><nav></code> role.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>Primary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>Breadcrumbs<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>navigation<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>Footer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>` </span></code></pre>
<p>Make sure your <code>aria-label</code> value is meaningful. In this next example, the info button label essentially just describes the button's icon:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- don't do this --></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>This makes me want to absquatulate. </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>question mark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>fa fa-question-circle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> </span></code></pre>
<p>A much better solution would be to provide relevant content about what the info button does or why it's here:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>This makes me want to absquatulate. </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>What does absquatulate mean?<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>fa fa-question-circle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="appropriate-uses-of-aria-labelledby">Appropriate uses of <code>aria-labelledby</code></h3>
<p>When suitable text for a label already exists elsewhere on the page, <code>aria-labelledby</code> is the preferred method for labeling (if you must use ARIA). Multiple <code>id</code> values from other elements are separated by a space within the <code>aria-labelledby</code>'s value to concatenate a single string of text.</p>
<p>Suppose you have a series of news articles on a page, and each article has a link that says "Read more." You can use <code>aria-labelledby</code> to add more context in order to differentiate between these generic links. Since <code>aria-labelledby</code> will overwrite the visible text content of the link, you'll need to add the "Read more" text back in as well.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>article1-heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>All About Dragons<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>I like dragons. Blah blah blah blah blah.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>article1-read-more<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>article1-read-more article1-heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Read more<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> </span></code></pre>
<p>This will result in the link being announced as "Read more All About Dragons." You could go one step better and <a href="https://www.nngroup.com/articles/learn-more-links/">rewrite those "Read more" links to be more descriptive for everyone</a>.</p>
<h2 id="when-should-i-skip-aria-for-labeling?">When should I skip ARIA for labeling?</h2>
<p>As has been noted by <a href="https://www.tpgi.com/short-note-on-aria-label-aria-labelledby-and-aria-describedby/">LƩonie Watson</a>, <a href="https://html5accessibility.com/stuff/2020/11/07/not-so-short-note-on-aria-label-usage-big-table-edition/">Steve Faulkner</a>, <a href="https://eevis.codes/blog/2021-11-29/aria-label-is-not-always-the-answer/">Eevis</a> and many others, these attributes do not work on every element. These labeling attributes are intended for:</p>
<ul>
<li>interactive elements</li>
<li>structural elements with landmark roles</li>
<li>widgets with explicit roles</li>
</ul>
<p>Check out this <a href="https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/#x5-6-accessible-name-guidance-by-role">handy chart of ARIA roles and their requirements for naming</a>.</p>
<p>Adding an <code>aria-label</code> will overwrite any visible text label that already exists, and adding an <code>aria-labelledby</code> will overwrite any visible text <em>AND</em> any <code>aria-label</code> provided. In general, you don't want to add <code>aria-label</code> when an appropriate visible text label already exists.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>about/<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>About Us<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>About Us<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>At first glance, this may seem harmless and it won't throw an error in automated or likely even manual accessibility testing, but what about when your company goes international? You're setting your future-self up for extra work and more headaches when that redundant non-visible label doesn't get translated or updated.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>about/<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>About Us<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sobre Nosotros<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>Never, ever do this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>run-button-1<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>Customize Report<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Run Report<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Sighted users will see the button's visible text label of "Run Report", but screen reader users will hear the button's accessible name of "Customize Report". Presumably "Run" and "Customize" are two different actions!</p>
<p>The problem in the previous example is not limited to screen reader users. Someone using voice control software might say "Run Report" to activate the control, but nothing will happen since that's not the accessible name. Voice control users will have to find a workaround when they need to perform the "Run" action that this button visibly indicates.</p>
<p>The following examples show some additional inappropriate uses of <code>aria-label</code>; you may as well save yourself some coding keystrokes and time spent debugging, by <strong>not</strong> doing any of the following bad examples.</p>
<h3 id="non-interactive-elements">Non-interactive elements</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</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>Product Group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>Don't put <code>aria-label</code> on a <code><div></code> or other non-interactive element without an explicit role; it does nothing but create tech debt.</p>
<h3 id="decorative-images">Decorative images</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>decorative-image.jpg<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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p>An empty <code>aria-label</code> attribute is not the same as an empty <code>alt</code> attribute. Use an empty <code>alt</code> attribute (<code>alt=""</code>) for decorative images.</p>
<h3 id="prohibited-roles">Prohibited roles</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>Causes of eye twitching<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>HTML elements whose implicit roles prohibit the use of <code>aria-label</code> or <code>aria-labelledby</code> are listed in the <a href="https://www.w3.org/TR/wai-aria-1.2/#namefromprohibited">current WAI-ARIA specifications</a>.</p>
<h2 id="what-about-aria-describedby?">What about <code>aria-describedby</code>?</h2>
<p>According to the <a href="https://www.w3.org/TR/wai-aria-1.1/#aria-describedby">WAI-ARIA specification for aria-describedby</a>, "a label should be concise, where a description is intended to provide more verbose information." Any <code>aria-describedby</code> content is announced by screen reading software after the element's label is read out.</p>
<p>The <code>aria-describedby</code> attribute should be used to provide supplemental content to an element's label ā in addition to the label ā rather than to label it directly. Examples of this might be to provide "helper text" about input format or other requirements when completing a form field, or to associate an error message with a specific input.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>textbox-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Date<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>textbox-1<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>description-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>description-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Use mm/dd/yyyy format<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> </span></code></pre>
<p>The use of <code>aria-describedby</code> in this example will let all users know the format of the date to enter in this text input. This will announce to a screen reader user when focus is placed on the form field.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<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>username-error<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>username-error<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username is required<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> </span></code></pre>
<p>In this example, <code>aria-describedby</code> will programmatically associate the error message with the text input field, which allows screen reader users to hear the error message when the form field is focused. Sighted users will see the error message next to the text input field when it appears.</p>
<h2 id="no-aria-is-better-than-bad-aria!">No ARIA is better than bad ARIA!</h2>
<p>If in doubt, leave it out! Unfortunately, it's too common to find seemingly well-intentioned ARIA labels that actually make things worse for folks using assistive technologies. Meaningful and accurate labels can benefit everyone, including screen reader users, voice control users, users with cognitive issues, and all other users!</p>
<p>Screen reader users don't need "extra" instructions like "click here to activate the button" or "extra" information such as "this is a checkbox" so don't add this type of content into element labels. ARIA labels are only available to browsers and screen reading software, and can cause frustration and extra work for a voice control user when a visible label that doesn't match the accessible name. Generally speaking, don't add labels using ARIA if it provides a different user experience to a particular user group. Don't treat users with a disability differently than you would treat other users.</p>
<p>If something is important enough to add using ARIA, consider whether it should be added for all users. <a href="https://ericwbailey.website/published/aria-label-is-a-code-smell/">Overuse or misuse of the <code>aria-label</code> attribute can be indicative of larger problems</a> within a code base or development processes. There is more than one way to write accessible code, but please be judicious in your use of ARIA for labeling purposes and just use native semantic HTML whenever possible.</p>
<h2 id="resources-for-further-reading">Resources for further reading</h2>
<ul>
<li><a href="https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/">ARIA Authoring Practices Guide (APG): Providing Accessible Names and Descriptions</a></li>
<li><a href="https://www.w3.org/TR/accname-1.2/#mapping_additional_nd_te">Accessible Name and Description Computation</a></li>
<li><a href="https://www.w3.org/TR/html-aria/#docconformance-naming">Requirements for use of ARIA attributes to name elements</a></li>
<li><a href="https://www.w3.org/TR/html-aria/">ARIA in HTML</a></li>
<li><a href="https://www.a11yproject.com/checklist/">The A11Y Project Checklist</a></li>
</ul>
<h2 id="one-last-thing">One last thing</h2>
<p>I want to express my sincere appreciation to <a href="https://ericwbailey.website/">Eric Bailey</a> for his excellent editorial suggestions on this article, and to <a href="https://www.matuzo.at/">Manuel Matuzovic</a> for putting this whole thing together (again!). Thank you both!</p>
Adding Complementary Performance Data to Your Site
2022-12-06T00:00:00Z
https://htmhell.dev/adventcalendar/2022/6/
<p>Getting performance data from real users can transform assumptions about how a user experiences a site into objective, actionable information.</p>
<p>In the last two years, there has been increased awareness of web performance thanks to Google's Core Web Vitals (CWV) metrics. These metrics are great and have nearly universal application, but it can also be helpful to add information specific to your site. Fortunately, it's easier than ever to collect this data thanks to features that have made their way into browsers in recent years.</p>
<h2 id="critical-landmarks">Critical Landmarks</h2>
<p>Using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver"><code>PerformanceObserver</code></a> API, you can get performance information about page navigation, in-page landmarks like how long it takes to parse the HTML, how long it takes to download and render specific resources, and it's even possible to measure server performance with the <a href="https://www.w3.org/TR/server-timing/"><code>Server-Timing</code></a> header.</p>
<p>It's possible you have a dedicated Real User Monitoring (RUM) vendor that takes care of logging this information for you, but if not, most site analytics packages allow you to define and log custom data.</p>
<p>There has been much written about <abbr title="Core Web Vitals">CWV</abbr>, but for the purpose of this article, I'll talk about data that complements <abbr title="Core Web Vitals">CWV</abbr> and make comparisons where necessary. If you want to track <abbr title="Core Web Vitals">CWV</abbr>, there is a <a href="https://github.com/GoogleChrome/web-vitals">package</a> and <a href="https://web.dev/vitals/">accompanying article</a> that covers it in detail.</p>
<p><em>Note:</em> In some cases below, I'm using a placeholder function called <code>sendToAnalytics</code> to indicate where you might want to send the data to your site analytics to get the measurements from real users. You'll need to check the docs of your specific analytics software to see how to send data for your use case.</p>
<h2 id="implementation">Implementation</h2>
<p>Using <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver/PerformanceObserver"><code>PerformanceObserver</code></a>, it's possible to get precise information about events that occur during the page lifecycle.</p>
<p>Browsers log activities, known as entries, automatically for certain things like the resource timing entry that happens when files like images are loaded or the navigation timing entry that happens when navigation occurs. Every browser implements a different set of entries. You can run <code>PerformanceObserver.supportedEntryTypes</code> in the browser's console to see what is supported in your browser.</p>
<p>It's also possible to add your own entries, which you'll see below. The ones that we'll deal with are:</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark"><code>performance.mark</code></a> which records a single, named timestamp</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure"><code>performance.measure</code></a> which calculates the difference between 2 performance marks i.e. a start and end mark.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceElementTiming">PerformanceElementTiming</a> - will record timing information about a specific element on the page, such as an image or text element like <code><h1></code> by using an <code>elementtiming</code> attribute on an element.</li>
</ul>
<h3 id="server-timing">Server Timing</h3>
<p>The aforementioned web-vitals package has a method to track <a href="https://web.dev/ttfb/">Time to First Byte (TTFB)</a>, which is a measure of how long it takes between the initial navigation and when a response is received from the server. This is considered a backend metric because it's impacted by network latency, the database queries that are run as part of the page's construction, and server memory.</p>
<p>However, TTFB is an aggregate metric. If you want more granularity to break down where time is being spent on the backend, you can use the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing"><code>Server-Timing</code></a> HTTP header.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">import</span> <span class="token punctuation">{</span> sendToAnalytics <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./sendToAnalytics"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">trackServerTiming</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">threshold <span class="token operator">=</span> <span class="token number">200</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">try</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token punctuation">{</span> name<span class="token operator">:</span> url<span class="token punctuation">,</span> serverTiming <span class="token punctuation">}</span> <span class="token keyword">of</span> performance<span class="token punctuation">.</span><span class="token function">getEntriesByType</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> <span class="token string">"navigation"</span></span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token punctuation">{</span> name<span class="token punctuation">,</span> duration <span class="token punctuation">}</span> <span class="token keyword">of</span> serverTiming<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">// we only care about data beneath the threshold</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>duration <span class="token operator">></span> threshold<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token function">sendToAnalytics</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> name<span class="token punctuation">,</span> duration<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span></code></pre>
<h3 id="head-blocking-time">Head Blocking Time</h3>
<p>As it relates to performance, the <code><head></code> of the document is especially sensitive and directly affects rendering. For example, CSS is a render blocking resource when it matches the device <code>media</code> attribute, and so is JavaScript when it lacks <code>async</code>, <code>defer</code>, or <code>type="module"</code> on the <code><script></code> tag.</p>
<p>There's even more of a performance hit when the assets are loaded from a third-party domain like Google Fonts or any of the popular JS CDNs. A page cannot render until the blocking assets in the head are downloaded, parsed, and in the case of blocking JavaScript, executed as well.</p>
<p>How much blocking code there is and where it's served from play a part in the initial render performance, but even the order of the tags matters. Harry Roberts has talked extensively about the <a href="https://www.smashingmagazine.com/2021/09/css-head-tag/">ideal order for tags in the <code><head></code></a> and even has a <a href="https://csswizardry.com/ct/">diagnostic stylesheet</a> to pinpoint issues. These resources will help remove the bottlenecks, and the PerformanceObserver will help track the before-and-after metrics.</p>
<p>This is done by setting performance marks at the start and end of the <code><head></code> and then using <code>performance.measure</code> to calculate the difference between the start and end marks.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width,initial-scale=1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Example page<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A great example of performance marks<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">'performance'</span> <span class="token keyword">in</span> window<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> performance<span class="token punctuation">.</span><span class="token function">mark</span><span class="token punctuation">(</span><span class="token string">'htmhell:documenthead:start'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- ...the rest of the tags for the head. e.g. style, link, script, and meta tags.<br /><span class="highlight-line"> Probably many lines...</span><br /> --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="highlight-line"> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token string">'performance'</span> <span class="token keyword">in</span> window<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> performance<span class="token punctuation">.</span><span class="token function">mark</span><span class="token punctuation">(</span><span class="token string">'htmhell:documenthead:end'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> performance<span class="token punctuation">.</span><span class="token function">measure</span><span class="token punctuation">(</span><span class="token string">'htmhell:documenthead'</span><span class="token punctuation">,</span> <span class="token string">'htmhell:documenthead:start'</span><span class="token punctuation">,</span> <span class="token string">'htmhell:documenthead:end'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span></code></pre>
<p>Sometimes third-parties add their own performance marks, so prefixing your marks as a namespace of sorts helps keep the chatter down in your analytics data. I like to follow the convention of <code>{site}:{component}:{description}</code>. That way, you can do some programmatic measurements by filtering through the groups with the naming convention.</p>
<h3 id="important-elements">Important Elements</h3>
<p>There's usually a piece of content that is considered the most important. It depends on the type of site and the template for what the most important element might be. Here are some examples of what is likely to be the most important element:</p>
<ul>
<li>Media: article headline or main image.</li>
<li>E-commerce: product image on the product details page.</li>
<li>Search pages: the link for the first result.</li>
<li>Home page of a marketing site: hero image.</li>
<li>Video site: the video poster image.</li>
</ul>
<p><abbr title="Core Web Vitals">CWV</abbr> has a metric called Largest Contentful Paint (LCP), but that is the largest visual element and not necessarily the most important. Also, the <abbr title="Largest Contentful Paint">LCP</abbr> element can change based on a number of factors such as <a href="https://www.speedcurve.com/blog/element-timing-one-true-metric/">viewport size</a> or be stolen away by prominent 3rd party content like ads or privacy consent modals.</p>
<p>Element Timing is currently only implemented in Chromium-based browsers, but it's worth tracking considering the significant market share that these browsers have, and more browsers should support it in the near future. It's as simple as adding an <code>elementtiming</code> attribute to the element you want to track.</p>
<p>According to <a href="https://wicg.github.io/element-timing/">the spec</a>, the relevant timing value depends on whether the element with the <code>elementtiming</code> attribute is an image or a text node.</p>
<p>For an image, it's the <code>loadTime</code> property that is important, while for a text node, it's the <code>renderTime</code> property that gives the relevant information.</p>
<p>This code below is responsible for logging both the head blocking time measurement we set earlier, as well as the element timing.</p>
<p>If you import the function and call <code>trackPerfMarks("htmhell")</code>, it will report all the elementtiming entries but will limit the <code>measure</code> and <code>mark</code> to <em>only</em> entries that were prefixed with <code>htmhell</code>.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">import</span> <span class="token punctuation">{</span> sendToAnalytics <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./sendToAnalytics"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">trackPerfMarks</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">prefix</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">const</span> types <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"measure"</span><span class="token punctuation">,</span> <span class="token string">"mark"</span><span class="token punctuation">,</span> <span class="token string">"element"</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> types<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">type</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">try</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">new</span> <span class="token class-name">PerformanceObserver</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">list</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> list<span class="token punctuation">.</span><span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">entry</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">===</span> <span class="token string">"element"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token function">sendToAnalytics</span><span class="token punctuation">(</span></span><br /><span class="highlight-line"> window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>href<span class="token punctuation">,</span></span><br /><span class="highlight-line"> entry<span class="token punctuation">.</span>identifier<span class="token punctuation">,</span></span><br /><span class="highlight-line"> entry<span class="token punctuation">.</span>loadTime <span class="token operator">||</span> entry<span class="token punctuation">.</span>renderTime</span><br /><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>entry<span class="token punctuation">.</span>name<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>prefix<span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token keyword">let</span> value <span class="token operator">=</span> entry<span class="token punctuation">.</span>startTime<span class="token punctuation">;</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">===</span> <span class="token string">"measure"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> value <span class="token operator">=</span> entry<span class="token punctuation">.</span>duration<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token function">sendToAnalytics</span><span class="token punctuation">(</span>window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>href<span class="token punctuation">,</span> entry<span class="token punctuation">.</span>name<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token punctuation">{</span></span><br /><span class="highlight-line"> type<span class="token punctuation">,</span></span><br /> <span class="token comment">/* `buffered: true` catches entries that happened<br /><span class="highlight-line"> * before the observer was registered, as well as future events</span><br /> */</span><br /><span class="highlight-line"> buffered<span class="token operator">:</span> <span class="token boolean">true</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span></span><br /><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"></span></code></pre>
<h2 id="final-considerations">Final Considerations</h2>
<p>You can see a demo of how all of this is put together in this <a href="https://codesandbox.io/s/htmhell-performance-0wr20r">CodeSandbox demo</a>. If you open your console, you will see the events getting recorded by our mock function. Unfortunately, CodeSandbox doesn't include the Server-Timing header, but if it did, it would pick up the data.</p>
<p>With application-specific data, you can use it to help form a complete picture of how users are experiencing a site. This can be used to inform your decisions on where to focus performance efforts or how to build a case for performance.</p>
<p>It's worth noting that I include the part of the script that registers the PerformanceObserver and sends the data to analytics from a deferred script file i.e. one with the <code>defer</code> attribute or <code>type="module"</code>, meaning. I might miss some data this way from visitors who didn't stick around long enough for the script to run, but I'd rather do that then load the script in a non-deferred way and have it block rendering.</p>
<p>This type of data is a permanent fixture in my analytics reporting because it helps to track performance over time.</p>
Reading the meter
2022-12-05T00:00:00Z
https://htmhell.dev/adventcalendar/2022/5/
<p>The <code><meter></code> element is a little known and rarely used semantic element. It's a non-interactive <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#form-associated_content">form element</a> that renders as a partially filled horizontal bar. Browsers provide user-agent styles, but the <code><meter></code> element can also be styled.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">max</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>130<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span></code></pre>
<p>The example above in Safari, Chrome and Firefox on MacOS.</p>
<p><img src="https://htmhell.dev/images/advent2022/7/meter-browsers.png" alt="The meter element rendered with slightly different shapes, sizes and colors in Safari, Chrome and Firefox." /></p>
<p><code><meter></code> is sometimes confused with the <code><progress></code> element as they are visually similar, but <code><meter></code> represents a current value in a known range, while <code><progress></code> represents something that is <em>in</em> progress, like a file upload.</p>
<p>The definition found <a href="https://html.spec.whatwg.org/multipage/form-elements.html#the-meter-element">in the HTML spec</a> includes some examples of potential uses.</p>
<blockquote>The meter element represents a scalar measurement within a known range, or a fractional value; for example disk usage, the relevance of a query result, or the fraction of a voting population to have selected a particular candidate.</blockquote>
<p>The spec also states that it should not represent an arbitrary range, meaning there should be a known maximum value.</p>
<h2 id="attributes">Attributes</h2>
<p>Six attributes determine how the <code><meter></code> element renders and its basic semantics. The only required attribute is <code>value</code>, and all attributes must be a valid <a href="https://www.freecodecamp.org/news/floating-point-definition/">floating point number</a> (e.g., 5, -22, 7.25).</p>
<p>The following attributes set the value and range.</p>
<ul>
<li><code>value</code> - Current value. Must be between <code>min</code> and <code>max</code>. It can be the only attribute if it's expressed as a decimal fraction (e.g., 0.65, which would represent 65%).</li>
<li><code>min</code> - Minimum value for the range. Must be less than <code>max</code>. Defaults to 0 if not specified.</li>
<li><code>max</code> - Maximum value for the range. Must be greater than than <code>min</code>. Defaults to 1 if not specified.</li>
</ul>
<p>The following attributes set boundary segmentation. The bar can render in different colors based on their value. They also can provide additional semantic information for screen readers to announce.</p>
<ul>
<li><code>low</code> - Upper boundary of the low end of the range, or where the low range ends. Must be greater than <code>min</code> and less than <code>high</code> or <code>max</code>.</li>
<li><code>high</code> - Lower boundary of the high end of the range, or where the high range begins. Must be less than <code>max</code> and greater than <code>low</code>.</li>
<li><code>optimum</code> - Indicates the optimum point in the range. If unspecified, the midpoint between minimum and maximum values is the optimum.</li>
</ul>
<p>A summation from the HTML spec ...</p>
<ul>
<li>minimum value ⤠actual value ⤠maximum value.</li>
<li>minimum value ⤠low boundary ⤠high boundary ⤠maximum value.</li>
<li>minimum value ⤠optimum point ⤠maximum value.</li>
</ul>
<p>All of the following are valid:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.65<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>35<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>85<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="semantics-and-labeling">Semantics and labeling</h2>
<blockquote>Authors are encouraged to include a textual representation of the gauge's state in the element's contents, for users of user agents that do not support the <code><meter></code> element. ā <a href="https://html.spec.whatwg.org/multipage/form-elements.html#the-meter-element">HTML spec</a></blockquote>
<p>There are several screen readers in use today, and without access to all of them it's hard to know how well <code><meter></code> is supported.</p>
<p>According to a <a href="https://webaim.org/projects/screenreadersurvey9/">survey of screen reader users</a> by WebAIM released in 2021, the most commonly used <a href="https://webaim.org/projects/screenreadersurvey9/#used">desktop/laptop screen readers</a> among respondents were JAWS and NVDA, and the most commonly used <a href="https://webaim.org/projects/screenreadersurvey9/#mobilescreenreaders">mobile screen reader</a> was VoiceOver. On desktop/laptop the most common <a href="https://webaim.org/projects/screenreadersurvey9/#browsercombos">browser and screen reader combinations</a> were JAWS and Chrome (32.5%) and NVDA and Chrome (16%).</p>
<p>In 2018 Scott O'Hara <a href="https://scottaohara.github.io/a11y_styled_form_controls/src/meter/">tested and documented</a> the effect of styling on the <code><meter></code> element:</p>
<blockquote>JAWS 2018 + Chrome (latest) — Chrome + JAWS will announce both styled and unstyled <code>meter</code> elements and their current value, with no indication of their low, high, or max values. If a <code>meter</code> is provided an accessible name by `aria-label` or <code>aria-labelledby</code>, it will be completely ignored by JAWS.</blockquote>
<blockquote>NVDA 2018.2.1 + Chrome (latest) — Chrome + NVDA do not have issues with announcing styled meters unless they are given an accessible name via aria-label or aria-labelledby. Doing so will result in NVDA only announcing the accessible name and none of the meter's current state.</blockquote>
<blockquote>NVDA 2018.2.1 + Firefox 63 (nightly) — NVDA will announce nothing but the accessible name to meter elements, regardless of if they are styled or not.</blockquote>
<blockquote>VoiceOver + Safari 11.1.1 & 12.0 on macOS High Sierra — VoiceOver will completely ignore a styled meter element unless it has an accessible name set by aria-label or aria-labelledby. But even then, VoiceOver will only announce the accessible name and none of the meter's current state.</blockquote>
<blockquote>VoiceOver + Safari on iOS 11.4 & 12.1 — VoiceOver will completely ignore the existence of meter elements, regardless of if they are styled or not.</blockquote>
<p>Using NVDA and Narrator via <a href="https://assistivlabs.com/">Assistiv Labs</a> and VoiceOver with Safari on MacOS and iOS we can we can see if support for the <code><meter></code> element has improved since 2018. The tests below use the following versions of screen readers and browsers:</p>
<ul>
<li>NVDA (2022.3) + Chrome (106)</li>
<li>NVDA (2022.3) + Firefox (105)</li>
<li>Narrator (21H2) + Edge (106)</li>
<li>VoiceOver + Safari MacOS 15.6.1</li>
<li>VoiceOver + Safari iOS 15.6.1</li>
</ul>
<p>Narrator (Windows) and VoiceOver (Mac) are included in their respective operating systems and <a href="https://www.nvaccess.org/download/">NVDA</a> is a free open source screen reader available for Windows. <a href="https://www.freedomscientific.com/products/software/jaws/">JAWS</a> is paid screen reader for Windows and is not included in these tests.</p>
<p>Using our original example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.65<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>35<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>85<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span></code></pre>
<p><a href="https://codepen.io/superterrific/pen/QWrJVPm">View at CodePen</a></p>
<ul>
<li>NVDA + Chrome announces "progress bar" + the value (e.g, "progress bar .65", "progress bar 10").</li>
<li>NVDA + Firefox announces "progress bar" + the value (e.g, "progress bar .65", "progress bar 10").</li>
<li>Narrator + Edge announces the value as a percent + "meter" (e.g., "65% meter").</li>
<li>VoiceOver MacOS announces the value as a percent + "level indicator" if <code>min</code> and/or <code>max</code> are the only attributes present (e.g., "65%, level indicator). If <code>low</code>, <code>high</code>, or <code>optimum</code> are present the value is announced as "critical value", "suboptimal value" or "optimal value" + "level indicator" (e.g., "optimal value, level indicator").</li>
<li>VoiceOver iOS announces the same as MacOS but does not include "level indicator" (e.g., "65%", "optimal value").</li>
<li>None of them explicitly announce the <code>min</code>, <code>max</code>, <code>low</code>, <code>high</code> or <code>optimum</code> values.</li>
</ul>
<h3 id="fallback-text">Fallback text</h3>
<p>Using fallback text within the <code><meter></code> is the textual representation mentioned in the spec. Adding fallback text to our original example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.65<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>65% complete<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>10 out of 100<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>30/100<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>35<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>35 out of 100 items<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>85<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>20 centimeters long out of 100 total<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span></code></pre>
<p><a href="https://codepen.io/superterrific/pen/QWrJVPm">View at CodePen</a></p>
<ul>
<li>NVDA + Chrome ignores fallback text and announces value only (e.g, "progress bar .65", "progress bar 10").</li>
<li>NVDA + Firefox announces progress bar + fallback text (e.g., "progress bar 65% complete").</li>
<li>Narrator + Edge ignores fallback text and announces value only (e.g., "65% meter").</li>
<li>VoiceOver MacOS announces fallback text + level indicator if <code>min</code> and/or <code>max</code> are the only attributes present (e.g., "65% complete, level indicator"). If <code>low</code>, <code>high</code>, and/or <code>optimum</code> are present the fallback text is announced + "critical value", "suboptimal value" or "optimal value" + "level indicator (e.g., "20 centimeters long out of 100 total, critical value, level indicator").</li>
<li>VoiceOver iOS announces the same as MacOS but does not include "level indicator" (e.g., "65% complete").</li>
</ul>
<p>In addition to NVDA + Chrome and Narrator + Edge, fallback text is also not announced in NVDA + Edge and MacOS + Chrome. Support for the feature likely comes from the browser engine rather than the screen reader given that both Chrome and Edge use Blink.</p>
<h3 id="labeling">Labeling</h3>
<p>Labeling can provide additional information or meet the requirements of your use case (e.g., you need a visible label). Using our original example and adding various labeling techniques:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>meter-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Tasks<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</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>meter-1<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.65<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<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>cookies eaten<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>10 out of 100<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>articles written<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>30/100<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>meter-2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Shows watched<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>35<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>meter-2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>meter-3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Length in centimeters<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>85<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<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>meter-3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span></code></pre>
<p><a href="https://codepen.io/superterrific/pen/QWrJVPm">View at CodePen</a></p>
<ul>
<li>NVDA + Chrome announces all labeling techniques (e.g., "Tasks, progress bar 0.65").</li>
<li>NVDA + Firefox announces all labeling techniques (e.g., cookies eaten, progress bar 10 out of 100).</li>
<li>Narrator + Edge announces all labeling techniques (e.g., "articles written, 13% meter")</li>
<li>VoiceOver MacOS announces all labeling techniques except <code>title</code> (e.g., "30/100, level indicator" without announcing "articles written").</li>
<li>VoiceOver iOS announces all labeling techniques including <code>title</code>.</li>
<li>Using <code>label</code> or <code>aria-label</code> added a double announcement of the label (e.g., "Tasks, 65% complete, tasks, level indicator" or "Tasks, Tasks 65% meter") for VoiceOver and Narrator.</li>
</ul>
<p>There have been a few of improvements in screen reader support for the <code><meter></code> element since 2018.</p>
<ul>
<li>NVDA as of version 2020.4 using an accessible name (<code>aria-label</code> or <code>aria-labeledby</code>) no longer voids announcement of the <code><meter></code>'s state.</li>
<li>VoiceOver on iOS no longer ignores an unstyled <code><meter></code> element.</li>
</ul>
<p>It's unknown if accessible names still cause JAWS to ignore the content of the <code><meter></code> element.</p>
<h2 id="color-contrast">Color Contrast</h2>
<p>Browsers provide user-agent styles for the <code><meter></code> element, and each browser uses slightly different colors. Generally the default colors don't provide sufficient contrast against a white background to pass Web Content Accessibility Guidelines (WCAG) AA 2.1 standard of at least a 3.1 contrast ratio between graphical elements and adjacent colors (<a href="https://www.w3.org/TR/WCAG21/#non-text-contrast">success criterion 1.4.11 non-text contrast</a>). A black or very dark background is the best bet for color contrast using the default styling.</p>
<img src="https://htmhell.dev/images/advent2022/7/meter-contrast.png" alt="Green, yellow and red meter bars against white and black backgrounds for Safari, Chrome and Firefox." loading="lazy" width="800" height="672" />
<p>Keep in mind there can be variations between operating systems and displays, as well as browsers. My machine (2017 iMac 5k retina with default calibration) produced the following results (<a href="https://docs.google.com/spreadsheets/d/1x530peUSnh5Ir_77pkmZf6yVFxK700pMqB8QdbeOZvU/edit?usp=sharing">Google sheet with hex colors and contrast ratios</a>):</p>
<ul>
<li>Black background
<ul>
<li>Chrome, Firefox, Safari - all three color passed.</li>
</ul>
</li>
<li>White background
<ul>
<li>Chrome - green and red passed, yellow failed.</li>
<li>Firefox - red passed, green and yellow failed.</li>
<li>Safari - all three colors failed.</li>
</ul>
</li>
</ul>
<p>Other items of note:</p>
<ul>
<li>Firefox provides borders that have at least 3.1 contrast ratio against a white background, but don't meet that contrast threshold with the adjacent fill color.</li>
<li>In Safari the unused portion of the bar is transparent against a dark background.</li>
<li>Using Windows High Contrast Mode the <code><meter></code> bar fill rendered using the "selected text" color regardless of whether or not it was styled.</li>
</ul>
<h2 id="using-optimum-to-render-different-colors">Using <code>optimum</code> to render different colors</h2>
<p>When using a combination of <code>min</code>, <code>max</code> and <code>value</code> the bar color will be green regardless of the value. When including any combination of <code>low</code>, <code>high</code> and <code>optimum</code> the bar colors can be changed without any additional styling.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter#attr-optimum">MDN sums up using optimum</a>:</p>
<blockquote>When used with the <code>low</code> attribute and <code>high</code> attribute, it gives an indication where along the range is considered preferable. For example, if it is between the <code>min</code> attribute and the <code>low</code> attribute, then the lower range is considered preferred. The browser may color the meter's bar differently depending on whether the value is less than or equal to the optimum value.</blockquote>
<p>An example:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token comment"><!-- Bar colors: yellow, green, yellow --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>40<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>90<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- Bar colors: red, yellow, green --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>85<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>85<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>40<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>85<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>90<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token comment"><!-- Bar colors: green, yellow, red --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>40<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>90<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span></code></pre>
<p><a href="https://codepen.io/superterrific/pen/KKRJwqW">View at CodePen</a></p>
<p>It's best to play around with your values to see how it behaves, but generally</p>
<ul>
<li>Using only <code>low</code> and <code>high</code> will render orange/amber or green.</li>
<li>Adding <code>optimum</code> can additionally render red.</li>
</ul>
<p>Daniel Aleksandersen discovered <a href="https://www.ctrl.blog/entry/html-meter-segment-boundaries.html">browsers can render the segment boundaries differently</a> depending on how they interpret the spec.</p>
<blockquote>Firefoxās implementation is consistent: the values of the attributes are excluded from the segments with the same name (exclusive). Like Firefox, Chrome and Safari exclude the <code>low</code> attribute value from the low segment. Unlike Firefox, Chrome and Safari include the <code>high</code> attribute value in the high segment (inclusive).</blockquote>
<p>There's a <a href="https://github.com/whatwg/html/issues/3520">bug filed against the spec</a> to call for clarification. In the meantime he's also <a href="https://www.ctrl.blog/entry/html-meter-segment-boundaries.html">discovered a workaround</a> if you encounter the issue.</p>
<h2 id="styling">Styling</h2>
<p>Considering contrast issues, potential rendering differences or the needs of your use case you may want to style the <code><meter></code> element.</p>
<p>In 2013 <a href="https://css-tricks.com/html5-meter-element/">CSS Tricks created a comprehensive post</a> that still seems to be considered the go-to for <code><meter></code> styling. A more <a href="https://uipencil.com/2022/08/30/how-to-create-a-meter-scale-from-pure-html-html-meter-tag/">recent post at UI Pencil</a> has a straightforward bar color, shape and size example. Creating the example from that post <a href="https://codepen.io/superterrific/pen/ZEoVyKW">at CodePen</a> allowed for screenreader testing to see how styling might impact screen reader support.</p>
<img src="https://htmhell.dev/images/advent2022/7/meter-styled.png" alt="Three meter bars styled with colors different than the default colors." loading="lazy" width="568" height="466" />
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Battery Life<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Current value = 15</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>15<span class="token punctuation">"</span></span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</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></span><br /><span class="highlight-line"> 15% battery remaining</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Current value = 59</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>59<span class="token punctuation">"</span></span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</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></span><br /><span class="highlight-line"> 59% battery remaining</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Current value = 88</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>88<span class="token punctuation">"</span></span> <span class="token attr-name">min</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">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</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></span><br /><span class="highlight-line"> 88% battery remaining</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p><a href="https://codepen.io/superterrific/pen/ZEoVyKW">View at CodePen</a></p>
<ul>
<li>NVDA + Chrome styling has no effect, announces "progress bar" + value (e.g., "Current value = 15, progress bar 15").</li>
<li>NVDA + Firefox styling has no effect, announces "progress bar" + fallback text (e.g., "Current value = 15, progress bar 15% battery remaining").</li>
<li>Narrator + Edge styling has no effect, announces value as percent + meter (e.g., "Current value = 15, 15% meter").</li>
<li>VoiceOver MacOS ignores the <code><meter></code> when styled, including fallback text (e.g., "Current value = 15"). Will announce an accessible name (e.g., <code>aria-label</code>) but add "empty group".</li>
<li>VoiceOver iOS ignores the <code><meter></code> when styled, including accessible name (e.g., <code>aria-label</code>).</li>
<li>Including an accessible name had no negative impact on NVDA and Narrator.</li>
</ul>
<p>The example above includes the text labels as <code><p></code> tags. Including the value in this way worked well for VoiceOver as it only announced the text (e.g., Current value = 15). The downside of this approach is that it added a bit of verbosity for NVDA and Narrator.</p>
<h2 id="use-case-examples">Use case examples</h2>
<p>The previously mentioned 2013 CSS Tricks article has a <a href="https://css-tricks.com/html5-meter-element/#aa-experiment-2-osx-style-disk-usage">disk space usage example</a> and accompanying <a href="https://codepen.io/pankajparashar/pen/ALgGqm">CodePen</a>. It's a good example of extending the <code><meter></code> element with styling while supporting people using assistive technology. The <code><meter></code> element semantically communicates the total disk usage while being styled to visually display the segments (audio, movies, photos, apps, other). If the <code><meter></code> information is not announced the adjacent text and unordered list ensures all the vital information is communicated.</p>
<p>Leslie Cohn Wein posted about a recent <a href="https://leslie.dev/posts/writing-semantic-html-even-when-you-dont-know-any-better/">budgeting use case</a> where talking through the scenario helped to clarify the semantics between <code><meter></code> and <code><progress></code>, and for her use case <code><meter></code> was the better choice.</p>
<p>Lea Verou recently posted about attempting to use the <a href="https://lea.verou.me/2022/08/on-ratings-and-meters/"><code><meter></code> element for star ratings</a>, and her explorations for creating a web component that's still a work in progress. Sven Wolfermann also created a <a href="https://codepen.io/maddesigns/pen/oQoMre">star rating example at CodePen</a> inspired by Verou's work.</p>
<h2 id="should-you-use-the-lessmetergreater-element?">Should you use the <code><meter></code> element?</h2>
<p>On first glance the <code><meter></code> element might seem straightforward for accessibility given that it's a semantic element, but testing has shown the support is not uniform.</p>
<p>If you're building something for an internal team where the all of the devices, browsers and assistive technology in use are known, you're likely to be in a much better position to make the <code><meter></code> element accessible to all. If you're building for a wider audience it's tougher to ensure.</p>
<p>Given the lack of sufficient contrast against light backgrounds, styling the <code><meter></code> element with at least a 3.1 contrast ratio against the background or adjacent colors is a good approach. Styled <code><meter></code> elements have relatively good support for desktop/laptop, but VoiceOver on iOS, a <a href="https://webaim.org/projects/screenreadersurvey9/#mobilescreenreaders">popular mobile screen reader</a>, ignores the <code><meter></code> when styled.</p>
<p>Using visually hidden text instead of fallback text or accessible names presents an option that can make using the <code><meter></code> element more accessible, regardless of whether or not it's styled.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</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">max</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 attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>65<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>65 out of 100 items<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meter</span> <span class="token attr-name">min</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">max</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 attr-name">low</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30<span class="token punctuation">"</span></span> <span class="token attr-name">high</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">optimum</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>85<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>meter</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>visually-hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>20 centimeters long out of 100 total<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p><a href="https://codepen.io/superterrific/pen/YzvQBNK?editors=1100">View at CodePen</a></p>
<p>Above code not styled:</p>
<ul>
<li>NVDA + Chrome announces "progress bar" + value + text (e.g., "progress bar 65. 65 out of 100 items").</li>
<li>NVDA + Firefox announces "progress bar" + value + text (e.g., "progress bar 65. 65 out of 100 items").</li>
<li>Narrator + Edge announces value as a percent + "meter" + text (e.g., "65% meter. 65 out of 100 items").</li>
<li>VoiceOver + MacOS announces value as percent + "level indicator" + text (e.g., "65% level indicator. 65 out of 100 items").</li>
<li>VoiceOver + iOS announces value as percent + text (e.g., "65%. 65 out of 100 items").</li>
</ul>
<p>Above code styled:</p>
<ul>
<li>NVDA + Chrome announces "progress bar" + value + text (e.g., "progress bar 65. 65 out of 100 items").</li>
<li>NVDA + Firefox announces "progress bar" + value + text (e.g., "progress bar 65. 65 out of 100 items").</li>
<li>Narrator + Edge announces value as a percent + "meter" + text (e.g., "65% meter. 65 out of 100 items").</li>
<li>VoiceOver + MacOS announces only text (e.g., "65 out of 100 items").</li>
<li>VoiceOver + iOS announces only text (e.g., "65 out of 100 items").</li>
</ul>
<p>This approach ensures announcement for mobile VoiceOver users and provides more consistently supported announcement than fallback text, albeit at the expense of some verbosity.</p>
<p>One issue to note with this approach is that there's a pause between the <code><meter></code>'s content and visually hidden text for the combinations that announce both. This may be confusing and not achieve the goal if it's unclear that the <code><meter></code>'s content and visually hidden text are related. Testing with screen reader users would help to clarify if this approach works.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In 2018 <a href="https://scottaohara.github.io/a11y_styled_form_controls/">Scott O'Hara said</a> of the <code><meter></code> and <code><progress></code> elements...</p>
<blockquote>Unfortunately, neither of these elements are consistently accessible to screen readers. Styling each can actually make them even more inaccessible</blockquote>
<p>There have been improvements since his <a href="https://scottaohara.github.io/a11y_styled_form_controls/src/meter/">initial tests</a>, but there are still inconsistencies and unknowns. And styling can still render the <code><meter></code> inaccessible for VoiceOver + Safari on MacOS and iOS.</p>
<p>If you are going to use the <code><meter></code> element, use it with caution and keep these things in mind:</p>
<table>
<thead>
<tr>
<th>Screen reader + browser</th>
<th>Announces fallback text</th>
<th>Announces accessible name</th>
<th>Styling breaks announcement</th>
<th>Announces extra information based on high, low, optimum</th>
</tr>
</thead>
<tbody>
<tr>
<td>NVDA + Chrome</td>
<td>No</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>NVDA + Firefox</td>
<td>Yes</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>Narrator + Edge</td>
<td>No</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>VoiceOver + Safari MacOs</td>
<td>Yes unless styled</td>
<td>Yes including when styled</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>VoiceOver + Safari iOS</td>
<td>Yes unless styled</td>
<td>Yes unless styled</td>
<td>Yes</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<ul>
<li>Don't assume the <code><meter></code> element's semantics are uniformly supported or announced across screen readers.</li>
<li>Pay attention to contrast. When using default colors a dark background is best.</li>
<li>If you're supporting light and dark mode or themes, make sure your <code><meter></code> bar fill colors work across the board.</li>
<li>If styling, assume it will break or nullify semantic support for assistive technology and provide semantic information (e.g., value, additional labeling) using methods with broad support.</li>
<li>Accessible names (<code>aria-label</code>, <code>aria-labeledby</code>) may cause some screen readers to ignore the <code><meter></code> element content and only announce the accessible name (<a href="https://scottaohara.github.io/a11y_styled_form_controls/src/meter/">source</a> - JAWS, and potentially other screen readers not tested here).</li>
<li>As always, it's best to test with a screen reader, and if you can hire screen reader users to test, even better.</li>
</ul>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://html.spec.whatwg.org/multipage/form-elements.html#the-meter-element">HTML Spec</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter">MDN</a></li>
<li><a href="https://scottaohara.github.io/a11y_styled_form_controls/src/meter/">Styled Meter Accessibility Tests 2018</a></li>
<li><a href="https://www.ctrl.blog/entry/html-meter-segment-boundaries.html">The HTML meter element and it's (undefined) segment boundaries</a></li>
</ul>
<h2 id="special-thanks">Special thanks</h2>
<p>Many thanks to <a href="https://ericwbailey.website/">Eric Bailey</a> for reviewing this post. His thoughtful feedback notably improved the outcome and increased my understanding. And a special thank you to <a href="https://www.matuzo.at/">Manuel Matuzovic</a> for his unwavering dedication to saving us all from HTMHell.</p>
Landmarks and where to put them
2022-12-04T00:00:00Z
https://htmhell.dev/adventcalendar/2022/4/
<p>Heading elements (<code>h1</code> through to <code>h6</code>) are used to give structure to the content of your page. They're important for SEO, make your pages more readable and, of course, also help people that use assistive technologies navigate through your page. Somewhat less known are landmark elements. These are elements that are used to define the structure of your entire HTML and you probably know some of them already: <code>nav</code>, <code>main</code> and <code>footer</code> are all landmark elements (well, most of the time. We'll get to that.)</p>
<p>Many of these elements were only introduced with HTML5 (compared to the headers that have been with us for as long as HTML existed) so if you haven't used them extensively yet, that's not that unusual. Let's figure out what they are and where you can (and can not) use them.</p>
<h2 id="landmarks-101">Landmarks 101</h2>
<p>When you look up landmark elements, you'll find that they also go by another name: sectioning elements. You can take that quite literally: they section off different parts of your page into unique semantic blocks.</p>
<p>Knowing which part of a website has which role is helpful to many. They can help browsers determine what part of the page to show in a reader mode, they let search engine know what pages on your site are most important (linked from the <code>nav</code>) and they let users natively move focus to the <code>main</code> content without you having to bring your own skip-links: assistive technologies can show a list of all landmarks and your visitors can use that to navigate across your page.</p>
<p><img src="https://htmhell.dev/images/advent2022/5/landmark.png" alt="VoiceOver on macOS showing the lis of landmarks for htmhell.dev." /></p>
<p>In other words, landmarks are a bunch of elements that help you define the structure of a page: you use them to show which set-of-links is the navigation (<code>nav</code>), what part of the page is the intro (<code>header</code>) and the main content (<code>main</code>). If you page contains a stand-alone piece of content you could put that in an <code>article</code> and if it contains secondary information you put that in an <code>aside</code>. And if something it its own sectioned-off part you use ...a <code>section</code>.</p>
<p>Here's an overview of the landmark elements in HTML, their ARIA role and what they mean:</p>
<ul>
<li><code>aside</code> (role: <code>complementary</code>) can be used to show content that is complementary to the main subject of the page. For example, links to related documents or meta info related to the main subject.</li>
<li><code>footer</code> (role: <code>contentinfo</code>) is where you put all the information about a page. Typically that's things like copyright info, related links, the author</li>
<li><code>form</code> (role: <code>form</code>) <em>can</em> be a landmark element if it has a accessible name (set with <code>aria-label</code>, <code>aria-labelledby</code> or <code>title</code> attributes)</li>
<li><code>header</code> (role: <code>banner</code>) is where your page's "introduction" goes. Things like your logo, search and main navigation all go in here.</li>
<li><code>main</code> (role: <code>main</code>) contains the main content or functionality of your page.</li>
<li><code>nav</code> (role: <code>navigation</code>) is where you provide navigational links. They can be for your entire website (think your main menu), but also for your current page (think table of contents).</li>
<li><code>section</code> (role: <code>region</code>) This is a "generic standalone section of a page". Essentially, if you have a part of the page that stands alone, try to go down this list and if none of them fit but it's still a separate part of the page, use a section. Like <code>form</code>s, it'll only be a landmark if it also has an accessible name.</li>
</ul>
<p>There is still one more landmark that we need to discuss: the <code>search</code> landmark. All the landmarks above are HTML elements with a specific landmark role, but the <code>search</code> landmark role has no associated HTML element. It only exists in ARIA. As you might guess, the search landmark is used to indicate search functionality and practically, you'd add a <code>search</code> role to a <code>form</code> element to change it from a generic form to a search form.</p>
<h2 id="where-(not)-to-put-them-the-ideal-web-page-structure">Where (not) to put them: the ideal web page structure</h2>
<p>Now that you know which landmarks there are, how do you use them in a web page? "The ideal web page structure" is quite a heavy promise, but we're limiting it to the ideal <em>landmark</em> structure. Here's what that looks like:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- logo etc --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</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>search<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span> <span class="token comment"><!-- if it exists --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- what your page is all about --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- complementary information --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- copyright notice etc --></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span> <span class="token comment"><!-- if you want to --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span></code></pre>
<p>Your page begins with the <code>header</code> and ends with the <code>footer</code>. Your navigation and search landmarks will usually go into the <code>header</code> element and can sometimes fit well in the <code>footer</code> element as well.</p>
<p>Directly after the header is the <code>main</code> content. This is the most important part of your page so you put it as high as possible. Directly after the <code>main</code> element you can put any complementary information that relates to your entire page.</p>
<p>What this does it create a "landmark outline" of your page that looks like this:</p>
<ul>
<li>Banner</li>
<li>Navigation</li>
<li>Search</li>
<li>Main</li>
<li>Complementary</li>
<li>Contentinfo</li>
<li>Navigation</li>
</ul>
<p>Notice that unlike a heading structure, your landmarks don't add any hierarchy to a page. Each section of your page is meant to be its own part. It also doesn't matter how many other elements you add around or inside them. You are free to wrap everything in as many divs as <s>your javascript framework requires</s> you need to style your page.</p>
<p>When you add landmark elements, you're changing the semantics of your web page so there are some key things to keep in mind:</p>
<h3 id="not-all-nesting-is-created-equally">Not all nesting is created equally</h3>
<p>Even though your landmarks don't add hierarchy, where you put your landmark elements will determine if they even are landmarks. Both <code>header</code> and <code>footer</code> elements are only landmarks if they are inside your <code>body</code> element, but not if they're inside other sectioning elements.</p>
<p>A <code>header</code>/<code>footer</code> in any of the following elements indicate they're the header or footer for just that sectioning element, and they are taken out of the landmark structure:</p>
<ul>
<li><code>article</code></li>
<li><code>aside</code></li>
<li><code>main</code></li>
<li><code>nav</code></li>
<li><code>section</code></li>
</ul>
<p>Depending on who you ask (the <a href="https://www.w3.org/WAI/ARIA/apg/example-index/landmarks/complementary.html">ARIA authoring practice guide</a>, in this case), adding an <code>aside</code> into any of the above is also a bad idea. If something is complementary then by definition it's not part of the <code>main</code> or <code>article</code>. This doesn't track with the official <a href="https://html.spec.whatwg.org/multipage/sections.html#the-aside-element">HTML5 specification</a>, that has examples of using <code>aside</code> for things like pull quotes in articles.</p>
<p>Neither is correct or wrong, but keep in mind though that the more landmarks you add, the longer it takes assistive tech to go through the list of landmarks and the more annoying it will be for your visitors.</p>
<h3 id="sometimes-just-adding-landmark-elements-isnt-enough">Sometimes just adding landmark elements isn't enough</h3>
<p>As mentioned in the overview of landmarks, just adding a <code>form</code> or <code>section</code> won't make them landmarks. You need to give them an accessible name or explicit role, and there's a few ways to do that:</p>
<ol>
<li>Adding a <code>aria-labelledby</code> attribute that contains the <code>id</code> of another element with the title of the form or section.</li>
<li>Adding a <code>aria-label</code> or <code>title</code> attribute to the element. The title attribute will also be visible on hover so keep that in mind.</li>
<li>Adding an explicit <code>role</code> attribute with <code>form</code> or <code>region</code> as the value.</li>
</ol>
<h3 id="you-cant-just-keep-adding-them">You can't just keep adding them</h3>
<p>The <code>banner</code> , <code>contentinfo</code> and <code>main</code> landmark roles should only be added to your page once. There is nothing in HTML5 that prevents your site from having multiple of them but from a semantic standpoint it doesn't make sense and for people using assistive technologies it's confusing. Your page should have one header, one footer and one main area. If you need more, maybe you just need another page.</p>
<h3 id="naming-landmark-elements">Naming landmark elements</h3>
<p>If you have multiple of the same elements, for example you use a <code>nav</code> element for your top-level website navigation, and also for the table of contents on a page, you should give each of them a unique accessible name. If you don't, then visitors using assistive technologies will not know which is which. You can add a name using <code>aria-labelledby</code>, <code>aria-label</code> or <code>title</code> attributes.</p>
<p>When adding a name to a landmark element, it can be very tempting to add the role as well because that's how you would describe it in natural speech. For example, for your top level navigation you might want to add an <code>aria-label</code> of "Main navigation".</p>
<p>If you do that however, what assistive technology will announce is "Main navigation <em>navigation</em>" and your visitor gets to hear that it's navigation twice. Assistive technogies already append the role, so you shouldn't. In this case, naming your navigation "Main" is enough.</p>
<h3 id="dont-just-replace-your-divs-with-sections">Don't just replace your <code>div</code>s with <code>section</code>s</h3>
<p>A div is not a button, and a section is not a div. When HTML5 just came out, a lot of advice boiled down to "Use section elements because they're more semantic than divs" but of course, that only works if the divs you replace had semantic value. For the most parts, your <code>div</code>s wont have any specific semantics: they don't tell the browser what each part of the structure is for.</p>
<p>And that's fine! You can use your divs for styling, to structure your DOM in such a way that you can use CSS and JavaScript to build what you want to build. And when you have a portion of the page that is it's own standalone or self-contained part, you can add a section (and make sure to name it).</p>
<h2 id="on-roles-and-elements">On Roles and Elements</h2>
<p>Throughout this article I've used Elements (e.g. <code>header</code>) and Roles (e.g. <code>banner</code>). So what's with those? Are they the same thing? Why do they have different names?</p>
<p>HTML5 is the official specification in terms of what elements have what semantic value. However, the HTML5 specification doesn't cover all possible semantics. To provide richer semantics, ARIA was created as a technology that sits on top of HTML that can help "plug the gaps".</p>
<p>It's generally better to use HTML where possible: the <a href="https://www.w3.org/TR/using-aria/#rule1">first rule of aria</a> is <em>don't use aria</em>:</p>
<blockquote>If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.</blockquote>
<p>Practically this means that instead of adding a <code>role="banner"</code> to your div to make it a header element, you should just use the <code>header</code> element.</p>
<p>So why does <code>role="banner"</code> exist? It exists for some very pragmatic reasons: While yes, using a header element is better, not all websites are started anew, and not all websites are built with modern systems. Some systems might have no way of adding a <code>header</code> element. In those systems, using a <code>role</code> attribute means that you can still provide the correct semantics.</p>
<p>If you write HTML you should always prefer to use the HTML elements and if you do, you don't have to add the <code>role</code> attribute (there are <a href="https://www.scottohara.me/blog/2019/01/12/lists-and-safari.html">some places where you should</a>, but they're not landmark-related).</p>
<p>As for the names being different: for better or for worse ARIA and HTML are written by different people in different places, with different needs. While for ARIA it makes sense to call the navigation "navigation", HTML authors would go crazy if for every menu they had to write <code><navigation></code> instead of just <code><nav></code>. The names in ARIA in comparison are written to also make sense outside of the concept of a HTML page. A page has a footer, but any bit of content might have "content info" that gives additional information.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Your page should almost certainly have a top level <code>header</code>, <code>main</code> and <code>footer</code>, but you don't want to add a bunch of them. If it's part of a larger website then you'll want to use a <code>nav</code> element to link to other pages. If you use multiple <code>nav</code> elements you'll want to name them so your visitor can tell them apart. You should try and use the HTML elements but if you absolutely can not, ARIA gives you the <code>role</code> attribute. Landmarks have a name and a role, and both get read out by assistive technologies.</p>
<p>Used resources:</p>
<p><a href="https://www.w3.org/WAI/ARIA/apg/example-index/landmarks/HTML5.html">https://www.w3.org/WAI/ARIA/apg/example-index/landmarks/HTML5.html</a></p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element#content_sectioning">https://developer.mozilla.org/en-US/docs/Web/HTML/Element#content_sectioning</a></p>
<p><a href="https://www.w3.org/TR/wai-aria-1.2/#landmark">https://www.w3.org/TR/wai-aria-1.2/#landmark</a></p>
<p><a href="https://html.spec.whatwg.org/">https://html.spec.whatwg.org</a></p>
Using SRI to protect from malicious JavaScript
2022-12-03T00:00:00Z
https://htmhell.dev/adventcalendar/2022/3/
<p>At some point of developing a website, there might come a time where we need to <a href="https://saptaks.blog/posts/progressive-enhancement-is-not-anti-js.html">progressively enhance using JavaScript</a>. There are few different options of how you add JavaScript. Firstly, we can write our own script using vanilla JS only, and self host the JavaScript file. However, often we find ourselves needing to use a JavaScript framework or library to make our life a little easier. We can definitely download all the JavaScript framework and libraries we are using and self host them in our server, but most framework websites recommend using a CDN.</p>
<h2 id="cdns-for-javascript-frameworks-and-libraries">CDNs for JavaScript frameworks and libraries</h2>
<p><a href="https://www.cloudflare.com/en-gb/learning/cdn/what-is-a-cdn/">CDN</a> or Content Delivery Networks are a group of servers which stores duplicate copies of data and serves them to the user from the server closest to them to improve performance. CDNs are often used to serve static files like CSS and JS files by frameworks and libraries. Developers used CDNs for serving JS files not only because CDNs are usually faster, but also since a lot of commonly used frameworks served via CDN used to already be cached in your browser. For example, letās say there are 3 websites - websiteA.com, websiteB.com. Both of these websites use jQuery. To use CDN, both of these websites can use the following code in their HTML file:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>https://code.jquery.com/jquery-3.6.1.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></span></code></pre>
<p>Now, when a user visits, websiteA.com for the first time, the browsers requests for the jQuery file from <code>https://code.jquery.com</code> CDN and used to cache it. So, when the user would visit websiteB.com, the jQuery file were already cached in the browser, thus improving performance a lot. However, most browsers have <a href="https://www.stefanjudis.com/notes/say-goodbye-to-resource-caching-across-sites-and-domains/">stoppped resource caching</a> to prevent tracking users across websites.</p>
<figure style="margin-bottom: 2.4rem">
<img src="https://htmhell.dev/images/advent2022/15/cdn-usage-ranking-desktop.png" alt="Bar chart which provides a view of CDN usage for mobile sites broken up for top 1,000, 10,000, 100,000, 1 million and 10 million popular sites as per Google CRUX data. For Top 1,000 sites the CDN adoption is 64%. For the top 10,000 sites, it's 63%. For the top 100,000 sites, it's 52%. For the top 1 million sites, it's 37%. For the top 10 million sites, it's 29%." />
<figcaption><a href="https://almanac.httparchive.org/en/2022/cdn#fig-3">CDN usage by site popularity</a> on mobile as reported in Web Almanac 2022</figcaption>
</figure>
<p>Even though this change removes the performance improvements from resource caching, CDNs are still usually faster and simpler for serving CSS and JS files and does continue to be used widely. According to Web Almanac 2022, 64% of top 1,000 websites use <a href="https://almanac.httparchive.org/en/2022/cdn">CDN</a> to serve various content.</p>
<h2 id="security-issue">Security issue</h2>
<p>Using CDN to serve JavaScript comes with certain risks. One of the reasons privacy and security focused users are bit squirmish about JS is because a malicious JS can really harm your user. It can range from being misused for tracking and surveillance to compromising users private data. So, if all websites use the same CDN to serve a JS file, an attacker now needs to gain control of only the CDN server instead of all individual servers. Once they gain control, they can inject malicious code into the JS file, and it gets served to all end users of websites that use the CDN.</p>
<h2 id="how-does-sri-help?">How does SRI help?</h2>
<p>At this point, the solution to this seems to be to self host all your JavaScript codes instead of using a CDN. But that would be a step backward in terms of performance and not the favourable solution in many situations. This is where <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity">Subresource Integrity (or SRI)</a> helps.</p>
<p>Subresource integrity is a way of telling the browser how to verify that the JS it has fetched from the CDN is an untampered JS file. SRI uses the very popular concept of file hashing to verify that the file is still the same. Letās look into what a <code><srcipt></code> code with Subresource integrity to fetch jQuery would look like.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><br /><span class="highlight-line"> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://code.jquery.com/jquery-3.6.1.js<span class="token punctuation">"</span></span></span><br /><span class="highlight-line"> <span class="token attr-name">integrity</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI=<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">crossorigin</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>anonymous<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>The above code is what you get if you go to <a href="https://releases.jquery.com/">https://releases.jquery.com/</a> and click on the release you want to use. Now letās break it down to see what it means.</p>
<h3 id="integrity">integrity</h3>
<p>The <code>integrity</code> attribute contains the file hash. The <code>integrity</code> attribute has 2 parts - the hashing algorithm and the hash of the file. In the above code, <code>sha256</code> is the hashing algorithm which tells the browser which algorithm to use while computing hash of the received file. The part after <code>sha256</code>- is the base64 encoding of the expected hash of the untampered file provided by the code author. So when the browser comes across the above code, it fetches the jQuery file from the CDN server, generates the hash for the file it received and compares it with the hash mentioned in the <code>integrity</code> attribute. If the hashes donāt match, then the browser will refuse to execute the script and throw a network error saying fetching of the resources failed.</p>
<h3 id="crossorigin">crossorigin</h3>
<p>Since the CDN resource is not present in the same origin, the <code>crossorigin</code> attribute must be present to make the <a href="https://www.w3.org/TR/SRI/#is-response-eligible-for-integrity-validation">response eligible for integrity validation</a>. Without the <code>crossorigin</code> attribute, the browser will load the script as if the <code>integrity</code> attribute was not present. We set the value of <code>crossorigin</code> attribute to <code>"anonymous"</code> to ensure that no user credentials or cookie informations are sent to the CDN server.</p>
<h2 id="how-to-generate-sri?">How to generate SRI?</h2>
<p>In most cases, the framework or library we are importing using <code><script></code> tag should have the SRI code already mentioned in their documentation. For example, as we saw before, if we go to jQueryās release page, and click on the release we want, the example code already has the <code>integrity</code> and <code>crossorigin</code> attribute values mentioned. Bootstrap also has the <code>integrity</code> and <code>crossorigin</code> attributes mentioned in their quickstart page. But there are many other frameworks that donāt in which case we will need to generate the hash by ourselves. Also, itās recommended to use SRI for the scripts written by the developer for the website specifically. There can always be a scenario where the attacker gets access to only certain files, so SRI will ensure that the resources served by the server are untampered.</p>
<figure style="margin-bottom: 2.4rem">
<img src="https://htmhell.dev/images/advent2022/15/sri-hash-function-usage.png" alt="Bar chart showing percent of HTML elements with SRI using various hash functions. SHA-384 is used in 58.4% of elements with SRI in desktop
websites and 60.7% of elements with SRI in mobile websites. SHA-256 is used in 32.4% of elements with SRI in desktop websites and
30.8% of elements with SRI in mobile websites. SHA-512 is used in 10.9% of elements with SRI in desktop websites and 9.9% of
elements with SRI in mobile websites." />
<figcaption>
<a href="https://almanac.httparchive.org/en/2022/security#subresource-integrity" rel="noopener noreferrer">Percentage usage of SRI</a> hash function as mentioned in Web Almanac 2022
</figcaption>
</figure>
<p>To create a hash function, there are 3 different hash functions that are supported for SRI - sha256, sha384 and sha512. We need to use one of these 3 functions to compute the hash for the <code>.js</code> files and <code>.css</code> files and then use those values. The easiest way of doing this is using <a href="https://www.srihash.org/">https://www.srihash.org/</a>. All we need to do is paste the link to the resource we want to generate a hash for, and the website generates the HTML that we need to add. For example, if we wanted to get the HTML for the script tag to import react, all we need to do is paste <a href="https://unpkg.com/react@18/umd/react.production.min.js">https://unpkg.com/react@18/umd/react.production.min.js</a> from Reactās CDN page in the input field, chose the hash function we want, and it generates the following output:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>https://unpkg.com/react@18/umd/react.production.min.js<span class="token punctuation">"</span></span> <br /><span class="highlight-line"> <span class="token attr-name">integrity</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sha384-tMH8h3BGESGckSAVGZ82T9n90ztNXxvdwvdM6UoR56cYcf+0iGXBliJ29D+wZ/x8<span class="token punctuation">"</span></span></span><br /> <span class="token attr-name">crossorigin</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>anonymous<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>But if you fancy doing it manually, especially for your local JavaScript files and CSS files, we can do that as well using openssl in a command line.</p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line">openssl dgst -sha384 -binary FILENAME.js <span class="token operator">|</span> openssl base64 -A</span></code></pre>
<p>The above code will generate the base64 encoded hash to be put in the integrity attribute. For example, if we wanted to create the hash for the same react file manually, firstly we will need to download the JS file from <a href="https://unpkg.com/react@18/umd/react.production.min.js">https://unpkg.com/react@18/umd/react.production.min.js</a>, then open a terminal and enter <code>openssl dgst -sha384 -binary react.production.min.js | openssl base64 -A</code>. This gives me the output <code>tMH8h3BGESGckSAVGZ82T9n90ztNXxvdwvdM6UoR56cYcf+0iGXBliJ29D+wZ/x8</code> for React 18.2.0. Since I used the hash function <code>sha384</code> and with the above output, the value of my <code>integrity</code> attribute will be <code>sha384-tMH8h3BGESGckSAVGZ82T9n90ztNXxvdwvdM6UoR56cYcf+0iGXBliJ29D+wZ/x8</code>. Hence, we get a HTML same as the one we get from <a href="http://srihash.org/">srihash.org</a></p>
<p>The reason I really love Subresource Integrity is because itās such an important feature that is baked using HTML attributes. It shows that a lot can be achieved, even in making a website secure just by learning HTML better. Just add an <code>integrity</code> attribute and <code>crossorigin</code> attribute to ensure that untampered JS framework codes are fetched. Also, SRI is supported by <code><link></code> tags as well so that one can ensure that the CSS files are also fetched untampered. In <a href="https://darknetdiaries.com/transcript/52/">episode 52 of Darknet Diaries</a>, Jack Rhysider and Jonathan Klijnsma discuss about how a tampered modernizr JavaScript file might have caused the data breach in British Airways, that could have been avoided using SRI</p>
You Don't Need ARIA For That
2022-12-02T00:00:00Z
https://htmhell.dev/adventcalendar/2022/2/
<p>In web development, writing semantic HTML is important for accessibility, and also provides some nice <a href="https://twitter.com/dennisl/status/1109875307601948672">side effects</a> such as supporting browser "reader" modes, SEO, graceful degradation, and exporting.</p>
<p>Implementing semantic HTML will also greatly reduce the need for <a href="https://www.w3.org/TR/wai-aria-1.1/">ARIA</a> (Accessible Rich Internet Applications). ARIA is a large set of HTML attributes to help accessibility for users of assistive technology, namely screen readers, by better supporting semantics not provided or not supported well via HTML.</p>
<p>ARIA usage certainly has its place. But overall, reduced usage of ARIA will, ironically, greatly increase accessibility. This is due to several reasons including:</p>
<ol>
<li>ARIA is very often implemented incorrectly. This is well known in the industry, but for data, reference the <a href="https://webaim.org/projects/million/#aria">WebAIM Million report</a> which states āHome pages with ARIA present averaged 70% more detected errors than those without ARIAā.</li>
<li>ARIA may not be supported well (even when implemented correctly) by the browser and/or screen reader, which unfortunately is often the case. Reference <a href="https://a11ysupport.io/">a11ySupport.io</a> and <a href="https://www.powermapper.com/tests/screen-readers/aria/">PowerMapper</a> for related information.</li>
<li>ARIA isn't magic (as some web professionals seem to think!). Designs must still be accessible, inclusive, and usable.</li>
<li>ARIA only provides semantics. It doesn't provide any functionality. It doesn't provide any keyboard interaction that's often expected from certain ARIA patterns (the tab panel for example). It doesn't solve accessibility issues outside semantics for assistive technology (such as alternative text, color contrast, reading order, clear label text, captions, etc.)</li>
</ol>
<p>The folllowing cases will illustrate how to use HTML properly so that ARIA is not required. In doing so, the techniques demonstrated will help developers follow the <a href="https://www.w3.org/TR/using-aria/#rule1">first rule of ARIA</a> (to use HTML first if possible), as well as providing more robust code, and most likely, a more accessible experience for the user.</p>
<h2 id="use-cases">Use cases</h2>
<p>The cases below are just a sampling of issues and include form labels, buttons, links, and images. I find them to be prevalent issues, and in fact I came across all of them āin the wildā (in live websites) over the last couple weeks. There is a bad version followed by a good one and then a note. For demonstration purposes, the code snippets are greatly simplified from the webpages in which they were found.</p>
<p>Remember that correcting the ARIA redundancies may seem trivial on the surface, but can go a long way in:</p>
<ul>
<li>reducing code complexity ā less code!</li>
<li>reducing maintenance issues ā for example, use of one text node rather than duplicated (also, less code!)</li>
<li>reducing accessibility errors due to value calculations by the browser ā for example, the developer may not understand the <a href="https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/#name_calculation">accessible name calculation</a> which can get a bit tricky.</li>
</ul>
<p>Now let's get to the use cases!</p>
<h3 id="form-labels">Form labels</h3>
<h4>Example 1</h4>
<p class="code-label">Bad: redundant <code>aria-label</code></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</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>Pickup<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Pickup<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>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>... checked=<span class="token punctuation">"</span></span><span class="token attr-name">"</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg...</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> Pickup</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p class="code-label">Good: use the inline text within the <code>label</code> (implicit association)</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Pickup<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>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>... checked=<span class="token punctuation">"</span></span><span class="token attr-name">"</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg...</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span> </span><br /><span class="highlight-line"> Pickup</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight"><strong>Note:</strong> Remove <code>aria-label</code> for the win! The <code>label</code> wrapped around the <code>input</code> sufficiently provides an accessible name for the input. </p>
<p><strong>Warning:</strong> this example implements implicit input labeling which may have issues with some assistive technology such as Dragon NaturallySpeaking. It's recommended to use explicit association as in Example 2.</p>
<h4>Example 2</h4>
<p class="code-label">Bad: unwarranted use of <code>aria-labelledby</code></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</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>...<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>sortBy<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sort By:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sortBy<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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Relevance<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Most Recent<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Value<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<p class="code-label">Good: use the <code>for</code> and <code>id</code> attributes for explicit association</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</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>...<span class="token punctuation">"</span></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>sortBy<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sort By:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>select</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>sortBy<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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Relevance<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Most Recent<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>option</span><span class="token punctuation">></span></span>Value<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>option</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight"><strong>Note:</strong> Use fundamental HTML (and not ARIA) for the win! The <code>for</code> and <code>id</code> attributes provide an accessible name for the input.</p>
<h3 id="image-in-button">Image in Button</h3>
<p class="code-label">Bad: redundant <code>aria-label</code> and unnecessary role</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Save $1 on Halloween candy and decor<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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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 attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1507288.jpg<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>Save $1 on Halloween candy and decor<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p class="code-label">Good</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>1507288.jpg<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>Save $1 on Halloween candy and decor<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight"><strong>Note:</strong> Remove two ARIA attributes (and duplicate text) for the win! The <code>alt</code> attribute provides the accessible name for the button.</p>
<h3 id="linked-text">Linked text</h3>
<p class="code-label">Bad: overwriting pertinent content</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>view location<span class="token punctuation">"</span></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>/foobar...<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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">data-qa</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>order-address-one<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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>100 W Main St<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">data-qa</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>order-address-city<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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Strangeville, OH 48000<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p class="code-label">Good: use the existing inline text</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/foobar...<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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">data-qa</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>order-address-one<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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>100 W Main St<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">data-qa</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>order-address-city<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>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Strangeville, OH 48000<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight"><strong>Note:</strong> There's no need to be overzealous here; the <code>aria-label</code> overwrites the address text as the link name, therefore the address text is not accessible to screen reader users. Linking the address text is sufficient.</p>
<h3 id="linked-images">Linked images</h3>
<p class="code-label">Bad: redundant <code>aria-label</code></p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>...<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>Earn Cash Back<span class="token punctuation">"</span></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>/foo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>...<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>Earn Cash Back<span class="token punctuation">"</span></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>foo.png<span class="token punctuation">"</span></span> <span class="token attr-name">width</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 attr-name">height</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></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p class="code-label">Good: use the alt text only</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>...<span class="token punctuation">"</span></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>/foo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>...<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>Earn Cash Back<span class="token punctuation">"</span></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>foo.png<span class="token punctuation">"</span></span> <span class="token attr-name">width</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 attr-name">height</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></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight"><strong>Note:</strong> Remove <code>aria-label</code> and use fundamental HTML for the win! The <code>alt</code> attribute provides the link content.</p>
<h3 id="decorative-images">Decorative images</h3>
<p class="code-label">Bad: <code>role</code> attribute used to denote decorative image</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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 attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>foo.jpg<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>a star, a twirl shape, a balloon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p class="code-label">Good: use an empty <code>alt</code> value</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>foo.jpg<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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight"><strong>Note:</strong> Remove the <code>role</code> attribute (and remove the alt value) for the win! The empty <code>alt</code> attribute denotes that this is a decorative image.</p>
<h3 id="script">Script</h3>
<p class="code-label">Bad: <code>aria-hidden</code> on script tag</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<p class="code-label">Good: nothing extra needed for accessibility</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span></span></code></pre>
<p class="highlight"><strong>Note:</strong> Remove <code>aria-hidden</code> for the win! Script content (inline JavaScript) isn't output by assistive tech.</p>
<h2 id="summary">Summary</h2>
<p>Removing ARIA redundancy can reduce code complexity, maintenance issues, and the chance of accessibility errors. Reducing reliance on ARIA makes code more simple and will most likely increase accessibility.</p>
<p>Writing semantic HTML is important for accessibility and also has other benefits. Semantic HTML will greatly reduce the need for ARIA, which is often implemented incorrectly and may not be fully supported. Be sure to use HTML first before using ARIA, such as when coding form labels, buttons and links, and decorative images.</p>
<h2 id="further-reading">Further reading</h2>
<ul>
<li><a href="https://web.dev/learn/accessibility/aria-html/">ARIA and HTML</a> on web.dev</li>
<li><a href="https://briefs.video/videos/what-is-aria-even-for/">What Is ARIA Even For Webbed Briefs</a> by Heydon Pickering</li>
<li><a href="https://www.a11yproject.com/posts/aria-has-perfect-support/">Myth: ARIA has perfect support</a></li>
<li><a href="https://talks.yatil.net/1jBftF/">ARIA Seriousā½ presentation</a> October 2019 by Eric Eggert</li>
<li><a href="https://webaim.org/articles/gonewild/">Web Accessibility Gone Wild</a> by WebAIM</li>
</ul>
How to transfigure wireframes into HTML
2022-12-01T00:00:00Z
https://htmhell.dev/adventcalendar/2022/1/
<p>Soon enough in your career as a web developer, you encounter the situation where a designer hands over a wonderful web design in all its large-screen glory. Your mission now is to transform it into code to present a prototype as soon as possible, starting with nothing but an empty text file.</p>
<h2 id="facts-questions-and-the-truth">Facts, questions and the truth</h2>
<p>This task comes with a few challenges and most of the time thereās no single correct way to do it. In this article, Iām going to describe how I personally approach these tasks.</p>
<p>Our goal for today is to build an HTML prototype for an online newspaper homepage, āThe Weekly Prophetā. The only information we have is the following wireframe:</p>
<a href="https://htmhell.dev/images/advent2022/8/01-wireframe-initial.jpg">
<img src="https://htmhell.dev/images/advent2022/8/01-wireframe-initial.jpg" alt="A wireframe of a newspaper called āThe weekly prophetā. Thereās a large header on top and below a horizontal navigation bar. The main content are four sections filled with articles: āTop storiesā, āLatest newsā, āMost viewedā, and āWhatās going on in Hogwarts?ā. At the end of the page thereās a section with a newsletter subscription form and a footer with three lists of links." width="1402" height="3072" />
</a>
<p>Thereās a lot to unpack here.</p>
<p>The first important fact is that in HTML thereās no such thing as placing elements on a page next to each other (we need CSSāCascading Style Sheetsāfor that). Thereās just <strong>one element at a time</strong>. When thereās no one-column layout for small screen sizes provided by a designer, we need to define an order of elements on our own.</p>
<p>The next big questions, especially when youāre new to web development, are: <strong>where to start now?</strong> How do I know which elements I need to build this? How should they be nested?</p>
<p>The truth is, it is probably hard to come up with a final version of the HTML code without ever thinking about CSS and its features, because <strong>a lot of markup decisions are based on what CSS can do</strong>. As this article is purely about HTML, the outcome is most likely not complete, but a good starting point from a semantics and accessibility point of view.</p>
<h2 id="semantics-and-accessibility">Semantics and accessibility</h2>
<p>HTML might look like an easy-to-learn language, but comes with a lot of (hidden) power. Itās the heart of every web page. The better you understand the meaning and functionality of certain tags and attributes, the better your code will be. Sure, you can build the whole newspaper homepage by just using <code>div</code> and <code>span</code> elements, but thatās not what weāre here for.</p>
<p>We are going to think aboutā¦</p>
<ul>
<li>the meaning of sections of the page</li>
<li>the documentās outline and hierarchy of the content</li>
<li>the interactivity of elements</li>
</ul>
<p>ā¦because when you build a solid foundation, everything you add on top (i.e., styling with CSS and interaction with JavaScript) will be better. When working <em>with</em> the features HTML gives you and not against them, you can save a lot of time and code (and money) down the road. And as a nice side effect, you automatically build an accessible product that will make your users happy and this is what our work should be all about.</p>
<h2 id="dont-think-outside-the-box">Donāt think outside the box</h2>
<p>Enough of the talking, letās start coding!</p>
<p>Every time I see a web design or a wireframe, I immediately start imagining boxes. <a href="https://mxb.dev/blog/the-css-mindset/#h-everything-is-a-rectangle">On a website, every element is a box</a>, so I start grouping elements that belong together, draw boxes around them, and give them names.</p>
<a href="https://htmhell.dev/images/advent2022/8/02-wireframe-landmarks.jpg">
<img src="https://htmhell.dev/images/advent2022/8/02-wireframe-landmarks.jpg" alt="The same wireframe of a newspaper called āThe weekly prophetā as described above. The important areas are highlighted and numbered from one to eight." width="1388" height="3072" />
</a>
<p>I identified the following eight areas in the wireframe:</p>
<ol>
<li>Header</li>
<li>Navigation</li>
<li>Section: Top Stories</li>
<li>Section: Latest News</li>
<li>Section: Most viewed</li>
<li>Section Whatās going on in Hogwarts?</li>
<li>Subscription form</li>
<li>Footer</li>
</ol>
<p>Sections three to six contain all the interesting articles from the main content of the page.</p>
<p>I donāt start coding a very specific element on the page, but always begin with the overall page structure, the large content elements and then work my way <strong>from the outside to the inside</strong>.</p>
<h2 id="step-1-a-minimal-html-page-starter">Step 1: A minimal HTML page starter</h2>
<p>I have a very small starter HTML template I always use when creating a new web page from scratch. It looks like this:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Webpage starter<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Hello World!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<p>Iām not going into every little detail here, there are <a href="https://www.matuzo.at/blog/html-boilerplate/">other articles and tutorials about that</a>. We are going to change the pageās title according to the text in the wireframe and focus on the documentās <code><body></code> from now on.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>The Weekly Prophet ā Magical News from the Wizarding World<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="step-2-landmarks">Step 2: Landmarks</h2>
<p>Now is the time to place our first content areas weāve already defined above in our HTML document. The great thing about HTML is that for all these areas we have semantic tags that describe exactly what the specific area means, so letās make use of them!</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> The Weekly Prophet</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Magical News from the Wizarding World<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Top Stories<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Latest News<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Most Viewed<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>What's Going on in Hogwarts?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Subscribe to our Owl Newsletter<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span></code></pre>
<p>When using semantic HTML tags like <code>header</code>, <code>nav</code>, <code>main</code>, and so on, we automatically define so-called <em>landmarks</em>, which are subsections of a page with a special role attached to them. This way, screenreader users, for example, can jump from one landmark to another and are enabled to get an overview of the page more quickly.</p>
<p>Sections are <a href="https://htmhell.dev/adventcalendar/2022/4/">only turned into landmarks when giving them an accessible name</a>, for example, by adding IDs to the <code>h2</code> headlines and using an <code>aria-labelledby</code> attribute on each section referencing the respective title.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>top-stories<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>top-stories<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Top Stories<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="step-3-reusable-sections">Step 3: Reusable sections</h2>
<p>Next, Iād like to create the āframeā of the page, that means everything except the main content. The header contains the current date, a search input field, and the title. When I look at the page, the first element that draws my attention is the title, so I would put that first in the HTML code. This way, a screenreader would start reading the title and not the date. I can change the order of elements according to the design later using CSS.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> The Weekly Prophet</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Magical News from the Wizarding World<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2022-09-01<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Thu, September 1st 2022<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search<span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Searchā¦<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- see below --></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="the-time-element">The time element</h3>
<p>To provide additional meaning, we can wrap the date with a <code><time></code> tag. With the additional <code>datetime</code> attribute we specify the exact date and make the value more accessible to e.g. search engines.</p>
<h3 id="the-search-field-form">The search <s>field</s> form</h3>
<p>The thing about wireframes and screen designs is that not all required HTML elements and attributes might be obvious at first sight. We only see one input field in the top right corner, but thereās a lot more functionality required. Just adding this one field is not enough. The search functionality might be added by JavaScript later, but wouldnāt it be nice if we had a fallback in case this doesnāt work? To achieve that, we need a form that is going to replace the search input from above:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">action</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/search<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>search<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Search articles<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>search<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>search-input<span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Searchā¦<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>q<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>submit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Submit search<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
<p>Using CSS, we can hide the elements we donāt want to display while still providing accessible and robust HTML code for everyone. Including an action and a submit button in the <code>form</code> makes it possible to send a search request without no JavaScript involved. The label helps screenreader users find out what the input field is all aboutāa placeholder cannot replace a label. We use the <code>role</code> attribute to specify a search landmarkāthis is the only landmark role without a dedicated HTML tag.</p>
<h3 id="the-navigation">The navigation</h3>
<p>One center piece of any website is <a href="https://web.dev/website-navigation/">the navigation</a>. Itās a place to start for the user to get an overview of the site and its subsections. To build a navigation, thereās no magic required, just a list of some good old anchor elements a.k.a. hyperlinks.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</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>Main<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/news<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>News<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/ministry-magic<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Ministry of Magic<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/gringotts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Gringotts<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/hogwarts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Hogwarts<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/quidditch<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Quidditch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
<p>I add an <code>aria-label</code> attribute to the navigation because there will be more <code>nav</code> elements in the footer; we need an indicator to distinguish the purpose of these navigation elements.</p>
<h3 id="the-website-footer">The website footer</h3>
<p>At the end of the page, we can find a newsletter form and additional navigation content, similar to the header.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Subscribe to our Owl Newsletter<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">action</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/subscribe<span class="token punctuation">"</span></span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">name</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name-input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>address-input<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Address<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>address<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>address-input<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>submit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Subscribe<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>footer-categories<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Top Categories<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>footer-categories<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>footer-company<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>The Company<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>footer-company<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>footer-spells<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Spells and Charms<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>footer-spells<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span></code></pre>
<p>The <code>aside</code> tag contains additional content that is not directly related to the main content. I could take it and place it on a different page and it would still make sense. Similar to the search field above, a form is required to collect user data for newsletter subscriptions. Make sure that every input field gets its own meaningful label.</p>
<p>The columns in the footer are wrapped by <code>div</code> elements. They donāt carry any semantic information, but Iām sure Iām going to need this structure later when creating a layout in CSS. I donāt fill the navigations with all the links, but you get the idea.</p>
<p>Using the <code>aria-labelledby</code> attribute, I describe the navigation and its content similar to the <code>aria-label</code> attribute used above. The difference this time is that I use the ID of the describing elementāthe headlines above in our caseāas the attribute value.</p>
<h2 id="step-4-thinking-in-components">Step 4: Thinking in components</h2>
<p>Next, Iād like to focus on the main content: the four sections containing all the interesting articles. Each section looks slightly different, but nonetheless, itās articles everywhere. They all have a lot in common. In the image below there are all different variants of articles where the same parts are highlighted in the same color:</p>
<ul>
<li>title (2)</li>
<li>image (1)</li>
<li>teaser text (3)</li>
<li>author (4)</li>
<li>date and time (4)</li>
</ul>
<a href="https://htmhell.dev/images/advent2022/8/03-wireframe-articles.png">
<img src="https://htmhell.dev/images/advent2022/8/03-wireframe-articles.png" alt="A drawing of the five different article layouts that can be found in the wireframe. The first one is the largest one and contains all elements that are described in the following section of this article. There are other layouts that contain only an image and a title or just a title and a teaser text." "="" width="2510" height="1576" />
</a>
<p>With a lot of libraries, frameworks, and design systems out there, we live in a component-driven web development world. When implementing a design, we try to find out which elements function the same and create reusable components for them. This is what I plan to do for the articles.</p>
<p>The first article in the āTop Storiesā section contains all parts listed above, so this is the HTML structure I would use for it:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>Students returning to hogwarts<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/path/to/image.jpg<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>A descriptive alternative text for the image<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>The day 73 kids waited the whole summer long is finally here: there first day at Hogwarts. The Sorting Hat is awaiting them in the castle alreadyā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Rita Skeeter<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>time</span> <span class="token attr-name">datetime</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2022-09-01<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>01/09/2022<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>time</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span></code></pre>
<p>All the other articles use a reduced version of the snippet above, depending on the section where they occur. Here are three particularities in the code snippet above Iād like to highlight:</p>
<h3 id="images-and-alternative-text">Images and alternative text</h3>
<p>Images that provide content should always have a meaningful alternative text that describes whatās in the image. This way, people who are visually impaired can consume the same content just another way. I always try writing good image descriptions right from the beginning to not forget about it when deadlines and launch dates come closer.</p>
<p>A common mistake is to just use the articleās title as alternative text, but itās not likely that the title describes what can be seen on the image. In case the image is just a decorative item that does not provide any useful content, add an empty alternative text (<code>alt=""</code>), so a screenreader will ignore it.</p>
<h3 id="the-document-outline">The document outline</h3>
<p>One problem I find on many websites on my daily journeys through the World Wide Web is a wrong order of headlines. People tend to choose the headline level (from one to six) based on the headlineās font size as defined by the user agentās (the browserās) style sheet and not their meaning.</p>
<p>In the end, it doesnāt matter whether the <code>h1</code> headline is smaller than the <code>h3</code> headline because the page is designed this way; what matters is that when only looking at the headlines on a page (by actually <em>looking</em> at the page or by using a screenreader that only <em>reads</em> the headlines), the user should get an idea of the content and its hierarchy. Imagine writing an article, a thesis, or a book and structuring your content into main sections, chapters, and sub-chaptersāitās the same principle for the web pages you create.</p>
<p>This is why I chose the <code><h3></code> tag for the article headline, because the <code>h1</code> headline is reserved for the page title āThe Weekly Prophetā (there should only be <em>one</em> <code><h1></code> on a web page), and the <code>h2</code> headlines are used as headlines for the content sections.</p>
<h3 id="multiple-headers-and-footers">Multiple headers and footers</h3>
<p>Unlike the <code>main</code> or the <code>h1</code> element, it is allowed to have several <code>header</code> and <code>footer</code> elements on a single page. The tags are not reserved solely for the page header and footer, but can be used in sub-components as well to define additional meaning for elements within so-called <em>sectioning content</em>. In our example, the sectioning content element is the <code>article</code> and we define a header for image and title and a footer for the meta information about the author and publish date. In this case, the header and footer arenāt landmarks, but add useful semantic information nonetheless.</p>
<h2 id="step-5-last-small-details">Step 5: Last small details</h2>
<p>In two of the article sections, we can find additional links to category sub-pages. These should be implemented as simple links.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/latest-news<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>View all articles<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/hogwarts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Read more hogwarts news<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>The wireframe is helpful here, as understandable link texts are already provided. Sometimes you have to work with designs that use just some generic texts for such links like āread moreā or similar. When there are a lot of links with the same text on a page, it gets hard to find out where these links lead to without further context. Make sure to always provide meaningful link texts that tell the user what to expect when clicking on it.</p>
<h3 id="one-more-thing">One more thing</h3>
<p>The section about the most viewed articles is about ranking. Therefore, Iād use an ordered list to wrap the articles that show up there.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Most Viewed<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ol</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span><span class="token comment"><!-- article content ---></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span><span class="token comment"><!-- article content ---></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token comment"><!-- and so on⦠--></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ol</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thatās it, weāre done, weāve built a fundamental HTML structure based on the given wireframe. š</p>
<p>Here are my key takeaways for you:</p>
<ul>
<li>always remember to use available semantic HTML tags to define the contentās meaning</li>
<li>use only one <code>h1</code> and <code>main</code> tag per page</li>
<li>take care of proper headline order, count from one to six and donāt skip levels</li>
<li>provide descriptive alternative texts for images (or leave the alt attribute empty when the image is just decorative)</li>
<li>use built-in functionality like links and forms for the user to interact with your site</li>
</ul>
<p>To make sure there are no syntax or semantic errors in your code, I recommend using tools like the <a href="https://validator.w3.org/">W3C validator</a> to run a quick check on your code. Sometimes you get helpful insights about whatās wrong and how to fix it.</p>
<p>Itās not that hard to take responsibility for building good HTML and consequently an accessible site, you can start with small first steps and make your way to the top from there.</p>
#1 button disguised as a link
2019-10-17T01:00:00Z
https://htmhell.dev/1-button-disguised-as-a-link/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>link<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Name of website<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 punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>Name of website<span class="token punctuation">"</span></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>logo.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Name of website<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>Wrong usage of the button element. Thereās an element for linking to external sites (<code><a></code>). Do not change native semantics, unless you really have to.</li>
<li>Itās possible to link to pages without JavaScript.</li>
<li>The <code>title</code> attribute is redundant.</li>
<li>The <code>tabindex</code> attribute is redundant. A button doesnāt need <code>tabindex</code>, itās focusable by default.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>Name of website<span class="token punctuation">"</span></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>logo.jpg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#2 div with button role
2019-10-17T02:00:00Z
https://htmhell.dev/2-div-with-button-role/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</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>-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>28<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>24<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>Setting button semantics explicitly using the <code>role</code> attribute isnāt necessary, thereās an element for that (<code>button</code>).</li>
<li>You donāt need the <code>tabindex</code> attribute if you use a <code>button</code>. HTML buttons are focusable by default.</li>
<li>A click event on a <code>div</code> triggers only on click. A click event on a <code>button</code> triggers on click and if the user presses the <kbd>Enter</kbd> or <kbd>Space</kbd> key.</li>
<li>Thereās no text alternative for the icon.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<p>Unfortunately, thereās no native way of hiding content only visually.<br />The <code>.sr-only</code> class makes sure that content is visually hidden but still accessible to screen reader users.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0 0 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> -1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Send<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>28<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>24<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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#3 image-buttons
2019-10-22T01:00:00Z
https://htmhell.dev/3-image-buttons/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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/edit.gif<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">openEditDialog</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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/delete.gif<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">openDeleteDialog</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>The purpose of the <code>img</code> element is to display images, not to execute JavaScript.</li>
<li>A click event on a <code>img</code> triggers only on click. A click event on a <code>button</code> triggers on click and if the user presses the <kbd>Enter</kbd> or <kbd>Space</kbd> key.</li>
<li>Thereās no text alternative for the image. Screen readers may announce the filename instead.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<h3 id="solution-#1-use-buttons-and-add-alt-attribute-to-images">Solution #1: Use buttons and add <code>alt</code> attribute to images</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">openEditDialog</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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/edit.gif<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>Edit product XY<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">openDeleteDialog</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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/delete.gif<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>Delete product XY<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="solution-#2-use-buttons-add-text-content-and-hide-images">Solution #2: Use buttons, add text content and hide images</h3>
<p>Unfortunately, thereās no native way of hiding content only visually.<br />The <code>.sr-only</code> class makes sure that content is visually hidden but still accessible to screen reader users.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0 0 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> -1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>An image with an empty <code>alt</code> attribute is not accessible to screen reader users, which in this case is desired, because thereās a screen reader accessible text alternative.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">openEditDialog</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Edit product XY<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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/edit.gif<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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">openDeleteDialog</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Delete product XY<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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/delete.gif<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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#4 link-also-button
2019-10-22T02:00:00Z
https://htmhell.dev/4-link-also-button/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Example<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>By nesting a button inside of a link, youāre sending two messages: this is a button, but also this is a link.</li>
<li>If youāre not sure when to use <code><a></code> or <code><button></code>, watch <a href="https://www.youtube.com/watch?v=8XjwDq9zG4I">The Links vs. Buttons Showdown</a> by <a href="https://twitter.com/marcysutton">Marcy Sutton</a>.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.button</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* use CSS to apply button-like styles to the link */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com<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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Example<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#5 button-like-link
2019-10-22T03:00:00Z
https://htmhell.dev/5-button-like-link/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>#form<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>button<span class="token punctuation">"</span></span> <span class="token attr-name">aria-haspopup</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token entity named-entity" title=" ">&nbsp;</span><span class="token entity named-entity" title=" ">&nbsp;</span>Register<span class="token entity named-entity" title=" ">&nbsp;</span><span class="token entity named-entity" title=" ">&nbsp;</span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<p>Itās a link to a form at the same page that looks like a button.</p>
<ol>
<li>By adding <code>role="button"</code> to a link, youāre telling that itās a button, though it acts like a link. Do not change native semantics, unless you really have to.</li>
<li><code>aria-haspopup="true"</code> suggests that thereās a popup, but it doesnāt exist.</li>
<li>Padding should be added via CSS, not <code>&nbsp;</code>.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.button</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token comment">/* use CSS to apply button-like styles to the link */</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>button<span class="token punctuation">"</span></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>#form<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Register <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#6 link with void operator as href value
2019-10-25T00:00:00Z
https://htmhell.dev/6-link-with-void-operator-as-href-value/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>javascript:void(1)<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span><span class="token value javascript language-javascript">window<span class="token punctuation">.</span>location<span class="token operator">=</span><span class="token string">"index.html"</span></span><span class="token punctuation">'</span></span></span><span class="token punctuation">></span></span>Link<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>Links won't work, if JavaScript fails to load or execute.</li>
<li>You donāt need JavaScript to link to other pages, you can use the <code>href</code> attribute for that. Browser support is pretty good (100% of all browsers).</li>
<li>The context menu on right click is different, "Open in new tab/window" is not available.</li>
<li><kbd>CMD</kbd> + Click to open a link in the background won't work.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>index.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Link<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#7 multiple duplicate ids and table layout
2019-10-29T00:00:00Z
https://htmhell.dev/7-multiple-duplicate-ids-and-table-layout/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</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>body<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</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>body<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</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>body<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</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>body_row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</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>body_left<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</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>body_middle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</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>body_right<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<p>An <code>id</code> should be unique no matter on which tag itās added. Also this code uses a table-based layout (and yeah, itās on a live production site still running, redesigned in 2016). Avoid using tables for layout reasons only, because table elements have a semantic meaning. Using them could make your document more confusing for some people.</p>
<ol>
<li>Replace the current markup with semantic HTML5 tags. This reduces the number of tags and avoids the table-based layout.</li>
<li>Style the new elements by using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox">Flexbox</a> or <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout">CSS Grid</a>.</li>
<li>For the ID values, more semantic terms should be used.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</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>body<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</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>secondary_content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</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>primary_content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</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>tertiary_content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#8 anchor tag used as button
2019-10-30T00:00:00Z
https://htmhell.dev/8-anchor-tag-used-as-button/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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 special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript">modal<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Login<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>If the <code>a</code> element has an <code>href</code> attribute, it represents a link to another resource like a page or a <abbr title="Portable Document Format">PDF</abbr> document.</li>
<li>The purpose of the element in this example is to trigger an action on the same page with JavaScript. The <code>button</code> element with the type button is more suitable because it has no default behaviour and itās designed to trigger actions on user input.</li>
<li>Browsers and devices that do not support JavaScript will not be able to access the content in the modal.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<h3 id="solution-#1-use-a-button-element">Solution #1: Use a button element</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript">modal<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Login<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p>Since the only purpose of this element is to trigger an action on the same page instead of navigation; the <code><button></button></code> element is the semantically correct element to use.</p>
<h3 id="solution-#2-add-a-valid-href-value-to-the-login-form">Solution #2: Add a valid href value to the login form</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/login<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript">modal<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Login<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>Another solution is to add a <code>href</code> value to a location where the same actions as the modal can be performed.<br />
This provides a fallback for browsers and devices that do not support JavaScript. This is an example of progressive enhancement.</p>
</div>
#9 Cookie Consent from Hell
2019-11-08T01:00:00Z
https://htmhell.dev/9-cookie-consent-from-hell/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>cookie_consent modal<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>We use cookiesā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>cookie_consent__close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>fa fa-times<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>cookie_consent__ok<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>OK<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>The modal is not the first item on the page and focus is not on the modal when the page loads. Keyboard users have to tab through all items on the page to access the cookie consent window.</li>
<li>A <code>div</code> isnāt keyboard focusable.</li>
<li>Content inside these <code>div</code>s is semantically just text. Assistive technology doesnāt know that these fake buttons are actually buttons.</li>
<li>A click event on a <code>div</code> triggers only on click. A click event on a <code>button</code> triggers on click and if the user presses the <kbd>Enter</kbd> or <kbd>Space</kbd> key.</li>
<li>Thereās no text alternative for the icon.</li>
<li><a href="https://fontawesome.com/v4.7.0/accessibility/">Font Awesome advises to hide icons semantically</a> by settings <code>aria-hidden="true"</code> on the <code><i></code> element.</li>
<li>Font Awesome adds Unicode content via the <code>::before</code> pseudo element. Assistive technology may announce the Unicode equivalent, which in this specific example would be ātimesā since <a href="https://fontawesome.com/icons/times">fa-times is not a cross but a multiplication sign</a>. (Please note: Talkback and VoiceOver didnāt announce anything in this example.)</li>
<li>Bonus: it should be possible to close modals by pressing <kbd>Esc</kbd>.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>cookie_consent modal<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cookie notice<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>We use cookiesā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>cookie_consent__ok<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>OK<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>cookie_consent__close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Close notification<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>fa fa-times<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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span>ā¦<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></span></code></pre>
<p>Unfortunately, thereās no native way of hiding content only visually.<br />The <code>.sr-only</code> class makes sure that content is visually hidden but still accessible to screen reader users.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0 0 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> -1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ol>
<li><a href="https://www.youtube.com/watch?v=Uaqo4FOI_DY">Screen readers and cookie consents</a></li>
</ol>
</div>
#10 <section> is no replacement for <div>
2019-11-22T01:00:00Z
https://htmhell.dev/10-section-is-no-replacement-for-div/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</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>page-top<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">data-section-id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page-top<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</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>main-content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</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>main-header<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>container-fluid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</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>content col-sm-12<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>content-inner<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>content__body<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</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>slider<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>slide<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li><a href="https://www.w3.org/TR/html52/sections.html#the-section-element">Sectioning content</a> (<code><article></code>, <code><aside></code>, <code><nav></code>, <code><section></code>) is content that potentially has a heading and is appropriate only if the elementās contents would be listed explicitly in the documentās outline.</li>
<li><a href="https://www.w3.org/TR/html52/sections.html#headings-and-sections">Itās OK to nest sectioning content</a>, but it only makes sense if the contents of the inner elements are related to the contents of the outer element.</li>
<li>In this specific example, the sectioning elements are used for styling purposes only. They must not convey any semantic meaning, most of these sections and articles should be divs.</li>
<li>Screen readers may announce the role of a labelled <code><section></code> (<em>region</em>), when a user navigates to this section. User Agents may also provide methods to navigate to section elements. Using too many (nested) sections may make interfaces for screen reader users unnecessarily complicated.</li>
<li><code><section></code>s are no replacement for <code><div></code>s.</li>
<li>A<code><header></code> typically only contains a group of introductory or navigational aids for its nearest ancestor <code><main></code> element or sectioning content or sectioning root element. If itās not a descendant of the main element or a sectioning content element, then that <a href="https://www.w3.org/TR/html52/sections.html#the-header-element">header is scoped to the body</a>.</li>
<li>The <a href="https://www.w3.org/WAI/tutorials/carousels/structure/">carousel (.slider) should be enclosed in a labeled region</a>, to allow users to find it easily.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>page-top<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">data-section-id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page-top<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</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>main-content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</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>main-header<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>container-fluid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>content col-sm-12<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>content-inner<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sliderheading<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>content__body<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>sliderheading<span class="token punctuation">"</span></span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>New Products<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>slider<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</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>slide<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="further-reading">Further reading</h2>
<ul>
<li><a href="https://a11ysupport.io/tech/html/section_element">Accessibility Support - section element (html)</a></li>
<li>Thanks to <a href="https://adrianroselli.com/">Adrian Roselli</a> for feedback</li>
</ul>
</div>
#11 The trigram for heaven
2019-11-29T01:00:00Z
https://htmhell.dev/11-the-trigram-for-heaven/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>nav-toggle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ā° Menu <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>A screen reader may announce this as <em>trigram for heaven menu</em>, because ā° is the unicode character for the <a href="https://en.wikipedia.org/wiki/Bagua">trigram for heaven</a>.</li>
<li>The purpose of the icon is decorative, it should be hidden from screen readers. Consider adding decorative images using background properties in CSS.</li>
<li>A click event on a <code>span</code> triggers only on click. A click event on a <code>button</code> triggers on click and if the user presses the <kbd>Enter</kbd> or <kbd>Space</kbd> key.</li>
<li>A <code>span</code> isnāt keyboard focusable, but this element must be focusable, because itās used for opening and closing the main navigation.</li>
<li><code>aria-expanded</code> must be added to indicate wheather the main navigation is collapsed or not.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>nav-toggle<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ā°<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> Menu</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="fun-fact">Fun fact</h2>
<blockquote>
<p>The Bagua or Pa Kua are eight symbols used in Taoist cosmology to represent the fundamental principles of reality, seen as a range of eight interrelated concepts. Each consists of three lines, each line either "broken" or "unbroken", respectively representing yin or yang. Due to their tripartite structure, they are often referred to as Eight Trigrams in English.<br />
<a href="https://en.wikipedia.org/wiki/Bagua">https://en.wikipedia.org/wiki/Bagua</a></p>
</blockquote>
</div>
#12 accessible poll yes/no
2019-12-03T16:00:00Z
https://htmhell.dev/12-accessible-poll-yesno/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</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>form<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Poll title<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>pollQuestion<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Is this accessible?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pollGroup<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>radiogroup<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radiogroup<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>Poll title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>poll<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pollQuestion<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[object Object]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>poll<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pollQuestion<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[object Object]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>No<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>poll<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pollQuestion<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[object Object]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Maybe<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>poll<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pollQuestion<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[object Object]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Can you repeat the question?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Vote<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>Setting form semantics explicitly using the <code>role</code> attribute isnāt necessary, the semantics are implied in the element.</li>
<li>A form is a landmark. An <code>aria-labelledby</code> referring to the <code>h2</code> gives the landmark an accessible name. This makes it more useful for navigation.</li>
<li>Setting <code>role="radiogroup"</code> isnāt necessary, and certainly not twice. If you want to group the element, use a <code>fieldset</code> instead.</li>
<li>Donāt use <code>aria-labelledby</code> to create a relationship between a <code>radiobutton</code> and the poll question. <code>aria-labelledby</code> is for setting the accessible name. Use a <code>legend</code> instead.</li>
<li>To give <code>radiobutton</code> an accessible name, put the <code>span</code> content in a <code>label</code>, and use <code>for</code> to create a relationship with the <code>radiobutton</code>.</li>
<li>The button is inside the fieldset to create one logical grouping.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>poll-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</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>poll-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Poll title<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>Is this accessible?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>radio1<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>poll<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>yes<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Yes<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>radio2<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>poll<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>No<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>radio3<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>poll<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>maybe<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Maybe<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio<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>radio4<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>poll<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>repeat<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>radio4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Can you repeat the question?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>Vote<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#13 link or label
2020-01-15T13:00:00Z
https://htmhell.dev/13-link-or-label/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>accept<span class="token punctuation">"</span></span> <span class="token attr-name">required</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>accept<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/legal<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> I accept the confidentiality policy and data⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>Itās bad practice to nest elements with <a href="https://www.w3.org/TR/html52/editing.html#activation-behavior">activation behavior</a> (e.g. click).</li>
<li>Users donāt expect a new page to open when they click a label.</li>
<li>The ability to click a label provides usability and accessibility benefits (larger hit area).</li>
<li>Place links outside the <code>label</code> element.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>accept<span class="token punctuation">"</span></span> <span class="token attr-name">required</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>accept<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> I accept the confidentiality policy and data⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line">(read <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/legal<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Terms and conditions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>)</span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://www.w3.org/TR/html52/sec-forms.html#the-label-element">4.10.4. The label element</a></li>
<li><a href="https://adrianroselli.com/2016/12/be-wary-of-nesting-roles.html">Be Wary of Nesting Roles by Adrian Roselli</a></li>
</ul>
</div>
#14 not my type
2020-01-17T07:00:00Z
https://htmhell.dev/14-not-my-type/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>button<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>button<span class="token punctuation">"</span></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>/signup<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>-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sign up<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>The <code>type</code> attribute has no effect on the semantics of an <code><a></code> element.</li>
<li>An anchor may have the <code>type</code> attribute, but the value should be a valid <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types">MIME type</a>. Browsers may consider it, but itās purely advisory.</li>
<li>If the presence of the <code>href</code> attribute makes sense, you most definitely want to use a proper link (<code><a></code>) and not a button, no matter how the element looks like in your design.</li>
<li>A negative <code>tabindex</code> value means that the element is not accessible via keyboard, but it could be focused with Javascript.</li>
<li>Do not change native semantics, unless you really have to.</li>
<li>If you need a button, use the <code><button></code> element.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/signup<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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sign up<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://www.w3.org/TR/html52/links.html#links">4.8.2. Links created by a and area elements</a></li>
<li><a href="https://www.w3.org/TR/html52/textlevel-semantics.html#the-a-element">4.5.1. The a element</a></li>
</ul>
</div>
#15 letter by letter
2020-01-24T07:00:00Z
https://htmhell.dev/15-letter-by-letter/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<p>Letters are wrapped in <code>div</code>s to animate each letter with JavaScript.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token property">text-align</span><span class="token punctuation">:</span> start<span class="token punctuation">;</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateX</span><span class="token punctuation">(</span>90deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>H<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateX</span><span class="token punctuation">(</span>90deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateX</span><span class="token punctuation">(</span>90deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateX</span><span class="token punctuation">(</span>90deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>d<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateX</span><span class="token punctuation">(</span>90deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>i<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateX</span><span class="token punctuation">(</span>90deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>n<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateX</span><span class="token punctuation">(</span>90deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>g<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>
<p>Assistive technology may announce the word letter by letter, if each letter is wrapped in an element.</p>
<div class="video"><iframe loading="lazy" width="700" height="394" src="https://www.youtube.com/embed/-Q4xk1QMex0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" title="VoiceOver demo: Each letter in a heading wrapped in a div"></iframe></div>
<a href="https://codepen.io/matuzo/pen/vYEbmxp">Code for this demo</a>
</li>
<li>
<p>Try to avoid excessive <abbr title="Document Object Model">DOM</abbr> sizes. Too many <abbr title="Document Object Model">DOM</abbr> nodes and nested <abbr title="Document Object Model">DOM</abbr> elements may harm your page performance.</p>
</li>
<li>
<p>A large <abbr title="Document Object Model">DOM</abbr> tree results in a large accessibility tree, which may have a bad impact on the performance of assistive technology.</p>
</li>
<li>
<p>The separation of presentation from content is advised. Move styles that donāt change dynamically into a CSS file.</p>
</li>
</ol>
</div>
<div class="section">
<h2 id="good-code-(solution-1)">Good code (Solution 1)</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span> Heading <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="good-code-(solution-2)">Good code (Solution 2)</h2>
<p>If you really have to.<br />
Provide an accessible version of the text for screen readers and hide the inaccessible text by using <code>aria-hidden="true"</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</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>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>H<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>d<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>i<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>n<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">transform-origin</span><span class="token punctuation">:</span> 50% 50% -30.8917px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>char<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>g<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span></code></pre>
<p>Unfortunately, thereās no native way of hiding content only visually.<br />The <code>.sr-only</code> class makes sure that content is visually hidden but still accessible to screen reader users.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.title</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token property">text-align</span><span class="token punctuation">:</span> start<span class="token punctuation">;</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.char</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateX</span><span class="token punctuation">(</span>90deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></span><br /><span class="highlight-line"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0 0 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>50%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">margin</span><span class="token punctuation">:</span> -1px<span class="token punctuation">;</span> <span class="token punctuation">}</span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://developers.google.com/web/tools/lighthouse/audits/dom-size">Uses An Excessive DOM Size</a></li>
<li><a href="https://www.technica11y.org/performance-and-the-accessibility-tree">Performance and the Accessibility Tree</a></li>
</ul>
</div>
#16 alt, no waitā¦, aria-label, no waitā¦, alt
2020-02-09T07:00:00Z
https://htmhell.dev/16-alt-no-aria-label-no-alt/
<div class="section bad">
<p>Context: A list of images that link to detail pages.</p>
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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 punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</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>Browser Wars: The Last Engine<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>Browser Wars: The Last Engine<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>Browser Wars: The Last Engine<span class="token punctuation">"</span></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>thumbnail.jpg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>If the <code><a></code> element has no <code>href</code> attribute, then the element represents a placeholder for where a link might otherwise have been placed.<br />
(<a href="https://htmhell.dev/16-alt-no-aria-label-no-alt/#foot-note1">HTML spec</a>)</li>
<li>If youāre adding a click event to a placeholder link, you probably donāt want to use a placeholder link, but an actual link with an <code>href</code> attribute or a <code><button></code>, depending on what's happening on click.</li>
<li>Placeholder links aren't focusable. <code>tabindex</code> makes them focusable, but the attribute is another indicator that a proper link would be a better choice here.</li>
<li><code>alt</code> is not allowed on <code>div</code> elements and it has no effect on their semantic meaning.</li>
<li>Avoid <code>aria</code> attributes when possible. The <code>aria-label</code> attribute on the <code>div</code> is redundant, because the <code>img</code> already has an accessible name (the value of the <code>alt</code> attribute).</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>detail.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>Browser Wars: The Last Engine<span class="token punctuation">"</span></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>thumbnail.jpg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://html.spec.whatwg.org/#the-a-element" id="foot-note1" rel="noopener">The a element</a></li>
<li><a href="https://www.scottohara.me/note/2019/07/17/placeholder-link.html">The accessibility of placeholder links </a></li>
</ul>
</div>
#17 inaccessible cards
2020-03-14T07:00:00Z
https://htmhell.dev/17-inaccessible-cards/
<div class="section bad">
<p>Context: A list of linked cards, each with heading, image, and teaser text.</p>
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Overview<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figure</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>card<span class="token punctuation">"</span></span> <span class="token attr-name">data-url</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image1.html<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">background</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>image1.jpg<span class="token punctuation">)</span></span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figcaption</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h4</span><span class="token punctuation">></span></span>My heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h4</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span>Teasertext...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figcaption</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figure</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figure</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>card<span class="token punctuation">"</span></span> <span class="token attr-name">data-url</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image2.html<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">background</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>image2.jpg<span class="token punctuation">)</span></span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figure</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>You might not need (so many) <code><section></code>s. Read <a href="https://www.smashingmagazine.com/2020/01/html5-article-section/">Why You Should Choose HTML5 <article> Over <section></a> by <a href="https://www.brucelawson.co.uk/">Bruce Lawson</a> for more details.</li>
<li>Heading levels shouldnāt be skipped. Screen reader users rely on a sound document outline and hierarchy. It helps with navigation and understanding how the page is structured.</li>
<li>The figure element represents content, optionally with a caption, that is self-contained, but in this example thereās no content, only a caption.</li>
<li>The image in a card usually isnāt decorative, it conveys information. It should be part of the HTML document and not added via CSS. Background images are not accessible to everyone.</li>
<li>The card is only linked via JavaScript. If thereās no proper HTML anchor (<code><a href="path/to/page"></code>), the ālinkā is inaccessible to screen reader and keyboard users.</li>
<li>The <code><h1></code> - <code><h6></code> elements represent introductory headings for their sections. The <code><h4></code> is flow content and thus technically allowed as a descendent of <code>figcaption</code>, but itās better to use it to introduce the card as a whole.</li>
<li>The <code>article</code> element represents a self-contained composition in a page or site. This could be a newspaper article, an essay or report, a blog or other social media post. For a simple paragraph use <code><p></code>.</li>
<li>Making accessible cards where the whole card is clickable isnāt easy. Read the articles in the <a href="https://htmhell.dev/17-inaccessible-cards/#resources">resources</a> section for more information.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Overview<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</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>card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>image1.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> My heading <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>image1.jpg<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>Description of image1<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Teasertext...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</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>card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://webaim.org/techniques/semanticstructure/">Semantic Structure</a></li>
<li><a href="https://adrianroselli.com/2020/02/block-links-cards-clickable-regions-etc.html">Block Links, Cards, Clickable Regions, Etc.</a></li>
<li><a href="https://inclusive-components.design/cards/">Inclusive Components: Cards</a></li>
<li><a href="https://justmarkup.com/articles/2020-02-21-teaser-with-multiple-links/">Teaser with multiple links</a></li>
<li><a href="https://css-tricks.com/block-links-are-a-pain-and-maybe-just-a-bad-idea/">Block Links Are a Pain (and Maybe Just a Bad Idea)</a></li>
</ul>
</div>
#18 main divigation
2020-03-22T07:00:00Z
https://htmhell.dev/18-main-divigation/
<div class="section bad">
<p>Context: The main navigation of a personal website.</p>
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>nav<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>about<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>thoughts<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>The <code><div></code> element is an element of last resort, for when no other element is suitable. Use of the <code><div></code> element instead of more appropriate elements leads to poor accessibility.</li>
<li>Use <code><nav></code> for the main navigation, it represents a <a href="https://www.scottohara.me/blog/2018/03/03/landmarks.html">landmark</a> with links to external or internal pages. Screen reader users may use shortcuts to access the navigation directly or skip it.</li>
<li>Use <code><ul></code> or <code><ol></code> to structure related links semantically and visually. Screen readers usually announce the number of items in a list.</li>
<li>If the order of items in the navigation matters, use <code><ol></code>, otherwise <code><ul></code>.</li>
<li>A click event on a <code>div</code> triggers only on click. Use <code><a href=""></code> to link to other pages. Itās (more) accessible to keyboard, screen reader, and mouse users than a fake JavaScript-only link.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>nav<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/about<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>about<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/thoughts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>thoughts<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://www.w3.org/TR/html52/sections.html#the-nav-element">4.3.4. The nav element</a></li>
<li><a href="https://www.w3.org/WAI/tutorials/menus/structure/">Menu Structure </a></li>
<li><a href="https://inclusive-components.design/menus-menu-buttons/">Menus & Menu Buttons </a></li>
</ul>
</div>
```
#19 heading in the wrong direction
2020-05-14T07:00:00Z
https://htmhell.dev/19-heading-in-the-wrong-direction/
<div class="section bad">
<p>Context: A simple page that displays the availability of a product.</p>
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Product Status<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Is the product available?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</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>message is-success<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Itās <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span><span class="token punctuation">></span></span>available<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li><code>h1 ā h6</code> elements must not be used to markup subheadings, subtitles, alternative titles and taglines unless intended to be the heading for a new section or subsection.<sup><a href="https://htmhell.dev/19-heading-in-the-wrong-direction/#resources">1</a></sup></li>
<li>All <code>div</code> elements in this specific example are superfluous. Itās likely that they only exist because a front-end framework adds them by default. Use <a href="https://reactjs.org/docs/fragments.html">Fragments in React</a> or similar techniques in other frameworks to avoid unnecessary markup.</li>
<li>Try to avoid excessive <abbr title="Document Object Model">DOM</abbr> sizes. Too many <abbr title="Document Object Model">DOM</abbr> nodes and nested <abbr title="Document Object Model">DOM</abbr> elements may harm your page performance.</li>
<li>A large <abbr title="Document Object Model">DOM</abbr> tree results in a large accessibility tree, which may have a bad impact on the performance of assistive technology.</li>
<li>Only <a href="https://www.w3.org/TR/html52/dom.html#phrasing-content">phrasing content</a> is allowed as children and descendants of <code>h1 ā h6</code> elements. (<code>h3</code> and <code>div</code> donāt fall in the phrasing content category).</li>
<li>The <code>i</code> element represents a span of text in an alternate voice or mood, or otherwise offset from the normal prose in a manner indicating a different quality of text.<sup><a href="https://htmhell.dev/19-heading-in-the-wrong-direction/#resources"><span class="u-hidden">Footnote</span>2</a></sup> If you just want italic text, use <code>font-style: italic;</code> in CSS.</li>
<li>If the <code><a></code> element has no <code>href</code> attribute, then the element represents a placeholder for where a link might otherwise have been placed. <sup><a href="https://htmhell.dev/19-heading-in-the-wrong-direction/#resources">3</a></sup></li>
<li>If youāre adding a click event to a placeholder link, you probably donāt want to use a placeholder link, but an actual link with an <code>href</code> attribute or a <code><button></code>, depending on what's happening on click.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Product Status<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Is the product available?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</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>message is-success<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Itās <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/product.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>available<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ol>
<li><a href="https://www.w3.org/TR/html52/common-idioms-without-dedicated-elements.html#common-idioms-without-dedicated-elements">4.13.1. Subheadings, subtitles, alternative titles and taglines</a></li>
<li><a href="https://www.w3.org/TR/html52/textlevel-semantics.html#the-i-element">4.5.22. The i element</a></li>
<li><a href="https://html.spec.whatwg.org/#the-a-element">4.5.1 The a element</a></li>
</ol>
</div>
```
#20 HTMHell special: close buttons
2020-05-23T07:00:00Z
https://htmhell.dev/20-close-buttons/
<div class="section">
<p>This first HTMHell special inspects one of the most complicated and most controversial patterns in front-end development:</p>
<p>š„ the close button. š„</p>
<p>In modals, ads, and other overlays you often find a button with a close symbol that allows users, or at least some of them, to close the overlay. This functionality is often limited to mouse users, because most implementations of close buttons suck.</p>
<p>After less than 2 hours of research, HTMHell presents a collection of 11 different bad practices.</p>
<h2 id="pattern-1-div-and-background-image">Pattern 1: div and background image</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.close::after</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">"close.png"</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="issues-and-how-to-fix-them">Issues and how to fix them</h3>
<ol>
<li>The <code><div></code> element is an element of last resort, for when no other element is suitable. Use of the <code><div></code> element instead of more appropriate elements leads to poor accessibility.</li>
<li>A click event on a <code>div</code> triggers only on click. A click event on a <code>button</code> triggers on click and if the user presses the <kbd>Enter</kbd> or <kbd>Space</kbd> key.</li>
<li>A <code>div</code> isnāt keyboard focusable.</li>
<li>Thereās no text alternative for the background image.</li>
<li>Screen readers announce: Nothing.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-2-div-and-icon">Pattern 2: div and icon</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ā</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-2">Issues and how to fix them</h3>
<ol>
<li>ā doesnāt represent <em>close</em> or <em>crossed out</em>, itās the multiplication sign, like in 2 ā (times) 2. Donāt use it for close buttons.</li>
<li>See Pattern 1 for details about the <code><div></code>.</li>
<li>Screen readers may announce: something like āmultiplication xā or ātimesā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-3-font-awesome-icons">Pattern 3: Font Awesome icons</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>fas fa-times<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.fa-times::before</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">'\f00d'</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-3">Issues and how to fix them</h3>
<ol>
<li>Screen readers may announce CSS generated content.<sup><a href="https://htmhell.dev/20-close-buttons/#resources"><span class="u-hidden">Footnote</span>1</a></sup></li>
<li><a href="https://fontawesome.com/v4.7.0/accessibility/">Font Awesome advises to hide icons semantically</a> by settings <code>aria-hidden="true"</code> on the <code><i></code> element.</li>
<li>Font Awesome adds Unicode content via the <code>::before</code> pseudo element. Assistive technology may announce the Unicode equivalent, which in this specific example would be ātimesā since <a href="https://fontawesome.com/icons/times">fa-times is not a cross but a multiplication sign</a>. (Please note: Talkback and VoiceOver didnāt announce anything in this example.)</li>
<li>The <code>i</code> element represents a span of text in an alternate voice or mood, or otherwise offset from the normal prose in a manner indicating a different quality of text.<sup><a href="https://htmhell.dev/20-close-buttons/#resources"><span class="u-hidden">Footnote</span>2</a></sup> If you just want italic text, use <code>font-style: italic;</code> in CSS.</li>
<li>See Pattern 1 for details about the <code><div></code>.</li>
<li>Screen readers may announce: ātimesā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-4-a-close-link">Pattern 4: A close link</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">a::after</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 28px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"Ć"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-4">Issues and how to fix them</h3>
<ol>
<li>If the <code>a</code> element has an <code>href</code> attribute, it represents a link to another resource like a page or a <abbr title="Portable Document Format">PDF</abbr> document.</li>
<li>The purpose of the element in this example is to trigger an action on the same page with JavaScript. The <code>button</code> element with the type button is more suitable because it has no default behaviour and itās designed to trigger actions on user input.</li>
<li>If youāre not sure when to use <code><a></code> or <code><button></code>, watch <a href="https://www.youtube.com/watch?v=8XjwDq9zG4I">The Links vs. Buttons Showdown</a> by <a href="https://twitter.com/marcysutton">Marcy Sutton</a>.</li>
<li>Screen readers may announce CSS generated content.<sup><a href="https://htmhell.dev/20-close-buttons/#resources"><span class="u-hidden">Footnote</span>1</a></sup> ā doesnāt represent <em>close</em> or <em>crossed out</em>, itās the multiplication sign, like in 2 ā (times) 2. Donāt use it for close buttons.</li>
<li>Screen readers may announce: ālink, timesā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-5-a-close-link-with-text">Pattern 5: A close link with text</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Close</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.close::before</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"\e028"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-5">Issues and how to fix them</h3>
<ol>
<li>Nice try, but itās still a link and not a button.</li>
<li>See Pattern 4 for details about <code><a></code> and CSS generated content.</li>
<li>Screen readers may announce: ālink, times closeā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-6-a-close-link-without-the-href-attribute">Pattern 6: A close link without the <code>href</code> attribute</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>close<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Ć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-6">Issues and how to fix them</h3>
<ol>
<li>Another nice try, but a link without <code>href</code> is still not a button.</li>
<li>If the <code><a></code> element has no <code>href</code> attribute, then the element represents a placeholder for where a link might otherwise have been placed.<sup><a href="https://htmhell.dev/20-close-buttons/#resources"><span class="u-hidden">Footnote</span>2</a></sup></li>
<li>If youāre adding a click event to a placeholder link, you probably donāt want to use a placeholder link, but an actual link with an <code>href</code> attribute or a <code><button></code>, depending on what's happening on click.</li>
<li>Placeholder links aren't focusable.</li>
<li>If youāre not sure when to use <code><a></code> or <code><button></code>, watch <a href="https://www.youtube.com/watch?v=8XjwDq9zG4I">The Links vs. Buttons Showdown</a> by <a href="https://twitter.com/marcysutton">Marcy Sutton</a>.</li>
<li>Screen readers may announce: ātimes, clickable.ā</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-7-placeholder-link-and-img">Pattern 7: Placeholder link and img</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>close.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-7">Issues and how to fix them</h3>
<ol>
<li>Thereās no text alternative for the image. Screen readers may announce the filename instead.</li>
<li>See Pattern 6 for details about placeholder links.</li>
<li>Screen readers may announce: āclose.png, image.ā</li>
</ol>
</div>
<div class="section bad">
<h2 id="pattern-8-radio-button">Pattern 8: Radio button</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</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>close<span class="token punctuation">"</span></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>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>close<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>radio<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">[type="radio"]</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-8">Issues and how to fix them</h3>
<ol>
<li>When accessibility advocates say ā<a href="https://www.youtube.com/watch?v=CZGqnp06DnI">Just use button</a>ā they mean the <code>button</code> element, not radio buttons.</li>
<li>Radio buttons are used in radio button groups describing <strong>a set of related options</strong>.</li>
<li>The SVG has no text alternative. Read <a href="https://www.deque.com/blog/creating-accessible-svgs/">Creating Accessible SVGs</a> by <a href="https://cariefisher.com/">Carie Fisher</a> to learn how to make SVGs accessible.</li>
<li><code>display: none</code> on the <code>input</code> make the <code>label</code> inaccessible, too.</li>
<li>Screen readers may announce: Nothing.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-9-button-with-icon">Pattern 9: Button with icon</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>close<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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Ć</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-9">Issues and how to fix them</h3>
<ol>
<li>ā doesnāt represent <em>close</em> or <em>crossed out</em>, itās the multiplication sign, like in 2 ā (times) 2. Donāt use it for close buttons.</li>
<li>Screen readers may announce: ātimes, buttonā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-10-button-with-svg">Pattern 10: Button with svg</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-10">Issues and how to fix them</h3>
<ol>
<li>The SVG has no text alternative. Read <a href="https://www.deque.com/blog/creating-accessible-svgs/">Creating Accessible SVGs</a> by <a href="https://cariefisher.com/">Carie Fisher</a> to learn how to make SVGs accessible.</li>
<li>Screen readers may announce: ābuttonā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-11-the-good-ol-x">Pattern 11: The good ol' X</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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 punctuation">></span></span>X<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-11">Issues and how to fix them</h3>
<ol>
<li>Setting button semantics explicitly using the <code>role</code> attribute isnāt necessary, thereās an element for that (<code>button</code>).</li>
<li>You donāt need the <code>tabindex</code> attribute if you use a <code>button</code>. HTML buttons are focusable by default.</li>
<li>See Pattern 1 for details about the <code><div></code>.</li>
<li>The letter X is not a close icon.</li>
<li>Screen readers may announce: āX, buttonā.</li>
</ol>
<blockquote>
Using "x" for your close buttons is like using salt in your coffee cause it looks the same as sugar.
</blockquote>
<cite><a href="https://twitter.com/mxbck/status/1187446513284325376">Max Bƶck</a></cite>
</div>
<div class="section">
<p>You can find all <a href="https://codepen.io/matuzo/pen/qBOvagg?editors=1100">bad practices on CodePen</a>.</p>
</div>
<div class="section">
<h2 id="alternatives">Alternatives</h2>
<h3 id="solution-1-a-button-with-visible-text-and-no-icon">Solution 1: A button with visible text and no icon.</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Close</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p><button type="button">Close</button></p>
<ol>
<li>Text only: easy to implement and comprehensible.</li>
<li>Screen readers may announce: āClose, buttonā.</li>
</ol>
</div>
<div class="section">
<h3 id="solution-2-a-button-with-visible-text-and-only-visually-accessible-icon">Solution 2: A button with visible text and only visually accessible icon.</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Close</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Ć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<button type="button">
Close
<span aria-hidden="true">Ć</span>
</button>
<ol>
<li>If you really have to use the times icon, hide it from screen readers by wrapping it in a <code>span</code> with <code>aria-hidden="true"</code>.</li>
<li>Screen readers may announce: āClose, buttonā.</li>
</ol>
</div>
<div class="section">
<h3 id="solution-3-a-button-with-hidden-text-and-only-visually-accessible-icon">Solution 3: A button with hidden text and only visually accessible icon.</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Close<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Ć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0 0 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> -1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<button type="button">
<span class="u-hidden">Close</span>
<span aria-hidden="true">Ć</span>
</button>
<ol>
<li>Unfortunately, thereās no native way of hiding content only visually.<br />The <code>.sr-only</code> class makes sure that content is visually hidden but still accessible to screen reader users.</li>
<li>Screen readers may announce: āClose, buttonā.</li>
</ol>
</div>
<div class="section">
<h3 id="solution-4-a-button-with-hidden-text-and-only-visually-accessible-icon">Solution 4: A button with hidden text and only visually accessible icon.</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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>Close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Ć<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<button type="button" aria-label="Close">
<span aria-hidden="true">Ć</span>
</button>
<ol>
<li>If you donāt want to show text on screen, provide a text alternative for your icon or SVG by adding <code>aria-label</code> to the button.</li>
<li>Screen readers may announce: āClose, buttonā.</li>
</ol>
</div>
<div class="section">
<h3 id="solution-5-font-awesome">Solution 5: Font Awesome</h3>
<p>For the sake of completeness, a close button with a Font Awesome icon.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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>close<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>Close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>fa fa-times<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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h3 id="general-remarks">General remarks</h3>
<p>Sometimes it makes sense to use more descriptive labels like āClose dialogā, āClose galleryā, or āClose adā.</p>
<p>If you use third party solutions for modals, dialogs, etc., please check how theyāve been implemented before you add them to your site. Donāt rely on others for code quality and accessibility.</p>
<p>You can find all <a href="https://codepen.io/matuzo/pen/zYvbmvm?editors=1100">close button best practices on CodePen</a>.</p>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ol>
<li><a href="https://tink.uk/accessibility-support-for-css-generated-content/">Accessibility support for CSS generated content</a></li>
<li><a href="https://html.spec.whatwg.org/#the-a-element">The a element</a></li>
<li><a href="https://www.scottohara.me/note/2019/07/17/placeholder-link.html">The accessibility of placeholder links </a></li>
</ol>
</div>
#21 Legendary legend!
2020-07-24T07:00:00Z
https://htmhell.dev/21-legendary-legend/
<div class="section bad">
<p>Context: A button that expands and collapses a section of text.</p>
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>panel-heading<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">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#collapse0<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span> Industries Served <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li><code>legend</code> is not allowed as a child of any other element than <code>fieldset</code>.<br />
(<a href="https://htmhell.dev/21-legendary-legend/#foot-note1">HTML spec for legend</a>)</li>
<li>You donāt need the <code>tabindex</code> attribute if you use a <code>button</code>. HTML buttons are focusable by default.</li>
<li><code>href</code> attribute is not allowed on the <code>button</code> element.<br />
(<a href="https://htmhell.dev/21-legendary-legend/#foot-note2">HTML spec for button</a>)</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>panel-heading<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Industries Served</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://html.spec.whatwg.org/#the-legend-element" id="foot-note1" rel="noopener">The legend element</a></li>
<li><a href="https://html.spec.whatwg.org/#the-button-element" id="foot-note2" rel="noopener">The button element</a></li>
</ul>
</div>
#22 the good olā div link
2020-10-14T07:00:00Z
https://htmhell.dev/22-the-good-ol-div-link/
<div class="section bad">
Context: A link to another page.
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>About us<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">onClick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript">location<span class="token punctuation">.</span>href<span class="token operator">=</span><span class="token string">'about.html'</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> About us</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">data-page</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>aboutus<span class="token punctuation">"</span></span> <span class="token attr-name">data-url</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>index.php<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> About us</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<p>ā¦or any other variation of this pattern where an element other than <code><a></code> is used to link to a page.</p>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>The <code><div></code> element is an element of last resort, for when no other element is suitable. Use of the <code><div></code> element instead of more appropriate elements leads to poor accessibility.</li>
<li>A click event on a <code>div</code> triggers only on click. A click event on an <code>a</code> triggers on click and if the user presses the <kbd>Enter</kbd> key.</li>
<li>A <code>div</code> isnāt keyboard focusable.</li>
<li>The context menu on right click doesnāt provide options like āOpen in new tab/windowā or āBookmark this linkā.</li>
<li>By default, screen readers just announce the text in a <code>div</code> (e.g. āabout usā), in a proper link (<code><a></code>) a screen reader announces the text and the role (e.g. āabout us, linkā).</li>
<li>Attributes like <code>aria-label</code> might not work (properly) on <code>div</code> elements.</li>
<li>Screen reader users may use a shortcut to list all links in a page. <em>ādiv-linksā</em> wouldnāt be listed, unless they have a role of ālinkā.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>aboutus.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> About us</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#23 A card pattern
2020-11-12T21:00:00Z
https://htmhell.dev/23-card-pattern/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Image<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/feature-teaser.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>Feature teaser<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Exciting feature!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span> This text describes what the feature does! <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/blog/feature<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Read more<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 9 12<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M.84 10.59L5.42 6 .84 1.41 2.25 0l6 6-6 6z<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>path</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>The example code uses an <code><article></code>. This element is meant for standalone content that can be re-used by itself. If there is anything reusable here, it is the entire ācardā. More appropriate is a <code><section></code>.</li>
<li>There is a piece of text before the image, with the text image. This seems to be some sort of definition of the role of the element following. When using proper semantic HTML, the HTML-elements already communicate their semantics. Adding text is superfluous and confusing.</li>
<li>The alt-attribute states that image is a teaser for the feature mentioned below. Adding the text teaser does not make it a teasing image. The visual function of the image does not work in code. The image adds nothing of value to the textual content of the card. An empty alt-attribute would have hidden the image (and made clear that it is decorative).</li>
<li>There is a <code><span></code> with text that is meant to be a heading. Assistive technology can make use of headings in the code. When this card is nested below an <code><h3></code>, this heading could be an <code><h4></code> instead.</li>
<li>The main paragraph inside the card is wrapped in a <code><div></code>. Using a <code><p></code> would communicate its intentions more clearly.</li>
<li>āRead moreā is not a very descriptive link text. It is especially bad when itās in a list with more links with the same text. The users does not know where this link leads.</li>
<li>The <code><svg></code> within the link doesnāt provide additional information and should be hidden from screen readers.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/feature-teaser.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><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h4</span><span class="token punctuation">></span></span>Exciting feature!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h4</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>This text describes what the feature does!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/blog/feature<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Read more about our exciting feature <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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>true<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 9 12<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M.84 10.59L5.42 6 .84 1.41 2.25 0l6 6-6 6z<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>path</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section">The Generic Section element</a></li>
</ul>
</div>
#24 A placeholder is not a label
2020-11-29T21:00:00Z
https://htmhell.dev/24-a-placeholder-is-not-a-label/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>First name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>Every form input element needs a label. When screen reader users access a form field, the label is announced with the field type (e.g. <em>first name, edit text</em>). If itās missing, users might not know what theyāre supposed to fill in (e.g. <em>edit text</em>).</li>
<li>Some screen readers fall back to <code>placeholder</code> as the label, but itās not recommended to rely on it.</li>
<li>By default, placeholder text is displayed in a light grey color with low contrast. It might not be readable for people with low vision or under certain conditions, like strong sunlight.</li>
<li>Itās possible to increase contrast by using the <code>::placeholder</code> pseudo element, but if contrast is too high, users may mistake a placeholder for data that was automatically filled in.</li>
<li>Using and displaying a <code><label></code> increases the target size of the form field which can be of great help, especially on touch devices.</li>
<li>If <code>placeholder</code> functions as the only label, the label disappears when the user types. This strains their short-term memory, especially on complex or rarely used forms.</li>
<li>Users cannot check what theyāve filled in before submitting a form, because they only see values and no labels.</li>
<li>If browsers auto-fill fields, users have to cut-and-paste auto-filled values to check if browsers filled in fields correctly.</li>
<li>Placeholder text is cut off if it goes beyond the size of the field.</li>
<li>Translation tools like Google Translate might not translate attribute values.</li>
<li>Labels work best when theyāre placed above the corresponding text field, not <em>in</em> the field.</li>
</ol>
<p>Check out the <a href="https://htmhell.dev/24-a-placeholder-is-not-a-label/#resources">resources section</a> at the bottom of this page for more.</p>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>firstname<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>firstname<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://www.nngroup.com/articles/form-design-placeholders/">Placeholders in Form Fields Are Harmful</a></li>
<li><a href="https://medium.com/simple-human/10-reasons-why-placeholders-are-problematic-f8079412b960">11 reasons why placeholders are problematic</a> by <a href="https://twitter.com/adambsilver">Adam Silver</a></li>
<li><a href="https://www.smashingmagazine.com/2018/06/placeholder-attribute/">Donāt Use The Placeholder Attribute</a> by <a href="https://ericwbailey.design/">Eric W. Bailey</a></li>
<li><a href="https://www.nngroup.com/articles/form-design-white-space/">Form Design Quick Fix: Group Form Elements Effectively Using White Space</a></li>
<li><a href="https://codepen.io/stevef/post/placeholder-the-piss-take-label">placeholder - the piss-take label</a> by <a href="https://twitter.com/stevefaulkner">Steve Faulkner</a></li>
<li><a href="http://www.webaxe.org/floated-labels-still-suck/">Floated Labels Still Suck</a></li>
</ul>
</div>
#25 A link is a button is a link
2021-04-30T21:00:00Z
https://htmhell.dev/25-a-link-is-a-button-is-a-link/
<div class="section">
Note: We've removed most classes to improve readability.
</div>
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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">type</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 attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/signup<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>link<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>focus<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>-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Sign up<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>icon icon-external-link<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>true<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 tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>You donāt need the <code>tabindex</code> attribute if you use an <code>a</code> tag. HTML hyperlinks are focusable by default.</li>
<li>The <code>type</code> attribute on <code>a</code> tag is used to hint at the linked URLās format with a MIME type, eg: <code>type="image/svg+xml"</code></li>
<li>Using <code>role=link</code> on an <code>a</code> tag is not needed since you already get that behaviour for free by using a standard hyperlink (<code><a href=""></code>).</li>
<li>A negative <code>tabindex</code> value means that the element is not accessible via keyboard, but it could be focused with Javascript</li>
<li>An additional <code>span</code> to handle focus isnāt necessary, <code>a</code> can do that by itself. šŖš»</li>
<li>The <code>i</code> element represents a span of text in an alternate voice or mood, or otherwise offset from the normal prose in a manner indicating a different quality of text.<sup><a href="https://htmhell.dev/25-a-link-is-a-button-is-a-link/#resources"><span class="u-hidden">Footnote</span>2</a></sup> If you just want italic text, use <code>font-style: italic;</code> in CSS.</li>
<li>If youāre using <code>aria-hidden</code> on an element, you donāt need to declare a <code>role</code>, because the element is inaccessible to screen reader users, anyway.</li>
<li>Try to avoid excessive <abbr title="Document Object Model">DOM</abbr> sizes. Too many <abbr title="Document Object Model">DOM</abbr> nodes and nested <abbr title="Document Object Model">DOM</abbr> elements may harm your page performance. A large <abbr title="Document Object Model">DOM</abbr> tree results in a large accessibility tree, which may have a bad impact on the performance of assistive technology. Only use extra <code>div</code>s and <code>span</code>s, if the design demands it.</li>
<li>Icon option 1: While <code>aria-hidden</code> can be useful to hide content that is not needed for screen readers (in this case an icon image), it's useful to add an <code>aria-label</code> when that content is meaningul for everyone, like declaring that an hyperlink will open in an external tab.</li>
<li>Icon option 2: The icon can be removed, because in the original snippet the link points to a page on the same site that opens in the same tab. The external link icon is intended to inform users that by clicking the link theyāre leaving the site.</li>
</ol>
<p>Check out the <a href="https://htmhell.dev/25-a-link-is-a-button-is-a-link/#resources">resources section</a> at the bottom of this page for more.</p>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/signup<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Sign up</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>fa fa-external-link<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 attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>External link<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<p>or</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/signup<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Sign up <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_link_role">Using the link role</a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Techniques/aria/ARIA24.html">Semantically identifying a font icon with role="img"</a></li>
</ul>
</div>
#26 HTMHell special: tasty buttons
2021-10-25T07:00:00Z
https://htmhell.dev/26-tasty-buttons/
<div class="section">
<p>The second HTMHell special focuses on another highly controversial pattern in front-end development:</p>
<p>š„ the burger button. š„</p>
<p>The burger button and his tasty friends (kebab, meatball and bento) usually reveal a list of links when activated. According to our studies, these buttons are highly optimized for mouse and touch users, but lack support for keyboard and screen reader users in most cases.</p>
<p>After less than 1 hours of research, HTMHell presents a collection of 18 different bad practices found on real websites.</p>
<h2 id="pattern-1-the-unsemantic-burger">Pattern 1: the unsemantic burger</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>burger<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.burger</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">justify-content</span><span class="token punctuation">:</span> space-between<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.burger span</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">background</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="issues-and-how-to-fix-them">Issues and how to fix them</h3>
<ol>
<li>The <code><div></code> element is an element of last resort, for when no other element is suitable. Use of the <code><div></code> element instead of more appropriate elements leads to poor accessibility. If you need a button, use the <code><button></code> element.</li>
<li>A click event on a <code>div</code> triggers only on click. A click event on a <code>button</code> triggers on click and if the user presses the <kbd>Enter</kbd> or <kbd>Space</kbd> key.</li>
<li>A <code>div</code> isnāt keyboard focusable.</li>
<li>Thereās no text label.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>Screen readers announce: Nothing.</li>
</ol>
<h3 id="other-variations-of-this-pattern">Other variations of this pattern</h3>
<h4>The empty burger</h4>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>burger<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<h4>The all div burger</h4>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>burger<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="pattern-2-the-classic-image-burger">Pattern 2: The classic image burger</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</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>menu<span class="token punctuation">"</span></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>menu.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-2">Issues and how to fix them</h3>
<ol>
<li>A click event on an <code>img</code> triggers only on click. A click event on a <code>button</code> triggers on click and if the user presses the <kbd>Enter</kbd> or <kbd>Space</kbd> key.</li>
<li>An <code>img</code> isnāt keyboard focusable.</li>
<li>Thereās no text alternative for the image. Screen readers may announce the filename instead.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>Screen readers may announce: menu.png, image.ā</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-3-the-modern-image-burger">Pattern 3: The modern image burger</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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>hamburger<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 20 20<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Open Navigation<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rect</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>9<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rect</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>15<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rect</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-3">Issues and how to fix them</h3>
<ol>
<li>A click event on an <code>svg</code> triggers only on click. A click event on a <code>button</code> triggers on click and if the user presses the <kbd>Enter</kbd> or <kbd>Space</kbd> key.</li>
<li>An <code>svg</code> isnāt keyboard focusable in most browsers (IE 11 is an exception).</li>
<li>The value of <code>aria-labelledby</code> references elements by their id, not by tag name.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>Screen readers interpret <code>svg</code> elements differently. To get a consistent result the <code>role="img"</code> attribute should be present on the <code>svg</code>.</li>
<li>Screen readers may announce: āOpen Navigation, graphicā or āOpen Navigation, group.ā</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-4-almost-a-burger">Pattern 4: Almost a burger</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>navbar-toggle<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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>icon-bar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>icon-bar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>icon-bar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-4">Issues and how to fix them</h3>
<ol>
<li>Thereās no text label.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>Screen readers announce: button.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-5-the-placeholder-link-burger-button">Pattern 5: The placeholder link burger button</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>menuButton<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-5">Issues and how to fix them</h3>
<ol>
<li>A link without <code>href</code> is still not a button.</li>
<li>If the <code><a></code> element has no <code>href</code> attribute, then the element represents a placeholder for where a link might otherwise have been placed.<sup><a href="https://htmhell.dev/26-tasty-buttons/#resources"><span class="u-hidden">Footnote</span>2</a></sup></li>
<li>If youāre adding a click event to a placeholder link, you probably donāt want to use a placeholder link, but an actual link with an <code>href</code> attribute or a <code><button></code>, depending on what's happening on click.</li>
<li>Placeholder links aren't focusable.</li>
<li>Thereās no text label.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>Screen readers announce: probably nothing.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-6-the-wannabe-button-burger-link">Pattern 6: The wannabe button burger link</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>button<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>menu<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>burger<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<ol>
<li>Do not change native semantics, unless you really have to.</li>
<li>If the <code><a></code> element has no <code>href</code> attribute, then the element represents a placeholder for where a link might otherwise have been placed.<sup><a href="https://htmhell.dev/26-tasty-buttons/#resources"><span class="u-hidden">Footnote</span>2</a></sup></li>
<li>If youāre adding a click event to a placeholder link, you probably donāt want to use a placeholder link, but an actual link with an <code>href</code> attribute or a <code><button></code>, depending on what's happening on click.</li>
<li>Placeholder links aren't focusable.</li>
<li>Screen readers may announce: āmenu, collapsed, buttonā</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-7-the-unsemantic-burger-with-extra-bacon">Pattern 7: The unsemantic burger with extra bacon</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>mobile-nav<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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Show nav</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-6">Issues and how to fix them</h3>
<ol>
<li>Pretty much the same as <a href="https://htmhell.dev/26-tasty-buttons/#pattern-1-the-unsemantic-burger">Pattern 1: The unsemantic burger</a> except that this button has a label which is good, but it also has <code>aria-hidden="true"</code> which means that it's completely inaccessible to screen reader users.</li>
<li>Screen readers announce: Nothing.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-8-the-javascript-link-burger-button">Pattern 8: The JavaScript link burger button</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</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>toggle-link<span class="token punctuation">"</span></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>javascript:<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</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>fa fa-reorder<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.fa-reorder::before</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"ļ"</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-7">Issues and how to fix them</h3>
<ol>
<li>Screen readers may announce CSS generated content.<sup><a href="https://htmhell.dev/26-tasty-buttons/#resources"><span class="u-hidden">Footnote</span>1</a></sup></li>
<li><a href="https://fontawesome.com/v4.7.0/accessibility/">Font Awesome advises to hide icons semantically</a> by settings <code>aria-hidden="true"</code> on the <code><i></code> element.</li>
<li>The <code>i</code> element represents a span of text in an alternate voice or mood, or otherwise offset from the normal prose in a manner indicating a different quality of text.<sup><a href="https://htmhell.dev/26-tasty-buttons/#resources"><span class="u-hidden">Footnote</span>2</a></sup> If you just want italic text, use <code>font-style: italic;</code> in CSS.</li>
<li>Thereās no text label.</li>
<li>If youāre not sure when to use <code><a></code> or <code><button></code>, watch <a href="https://www.youtube.com/watch?v=8XjwDq9zG4I">The Links vs. Buttons Showdown</a> by <a href="https://twitter.com/marcysutton">Marcy Sutton</a>.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>Screen readers announce: ālinkā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-9-the-"i-have-no-idea-what-im-doing-lets-just-add-some-attributes"-burger-button">Pattern 9: The āI have no idea what I'm doing, let's just add some attributesā burger button</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>mega-toggle-block<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 punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>mega-toggle-label<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>button<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</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> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-8">Issues and how to fix them</h3>
<ol>
<li>The <code><div></code> element is an element of last resort, for when no other element is suitable. Use of the <code><div></code> element instead of more appropriate elements leads to poor accessibility. If you need a button, use the <code><button></code> element.</li>
<li>A click event on a <code>div</code> triggers only on click. A click event on a <code>button</code> triggers on click and if the user presses the <kbd>Enter</kbd> or <kbd>Space</kbd> key.</li>
<li>The <code>div</code> is focusable, but it has no <code>role</code> or label.</li>
<li>The <code>span</code> tries to mimic a <code>button</code>, but it's not focusable and it has no label.</li>
<li>You donāt need the <code>tabindex</code> attribute if you use a <code>button</code>. HTML buttons are focusable by default.</li>
<li>Thereās no text label.</li>
<li>Screen readers may announce: āgroupā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-10-the-heavenly-anchor-link-burger-button">Pattern 10: The heavenly anchor link burger button</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>#menutoggle<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>menutoggle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ā°<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-9">Issues and how to fix them</h3>
<ol>
<li>A screen reader may announce this as <em>trigram for heaven</em>, because ā° is the unicode character for the <a href="https://en.wikipedia.org/wiki/Bagua">trigram for heaven</a>.</li>
<li>The purpose of the icon is decorative, it should be hidden from screen readers. Consider adding decorative images using background properties in CSS.</li>
<li>An anchor link is not a <code><button></code>.</li>
<li>An anchor link should not refer to itself.</li>
<li>If youāre not sure when to use <code><a></code> or <code><button></code>, watch <a href="https://www.youtube.com/watch?v=8XjwDq9zG4I">The Links vs. Buttons Showdown</a> by <a href="https://twitter.com/marcysutton">Marcy Sutton</a>.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>Screen readers announce: ātrigram for heaven, linkā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-11-the-bento-checkbox-button">Pattern 11: The bento checkbox button</h2>
<style>
[data-component="burger"] label {
background-image: url(data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxwYXRoIGQ9J00zIDNoNHY0SDN6bTE0IDBoNHY0aC00em0tNyAwaDR2NGgtNHptLTcgN2g0djRIM3ptMTQgMGg0djRoLTR6bS03IDBoNHY0aC00em0tNyA3aDR2NEgzem0xNCAwaDR2NGgtNHptLTcgMGg0djRoLTR6JyBmaWxsPScjQTdBQUIyJy8+PC9zdmc+);
width: 30px;
height: 30px;
display: block;
}
[data-component="burger"] input {
position: absolute;
opacity: 0;
pointer-events: none;
left: -9999px;
top: -9999px;
}
</style>
<p data-component="burger">
<input type="checkbox" id="burger" />
<label for="burger"></label>
</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>bento<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bento<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>bento<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.bento label</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxwYXRoIGQ9J00zIDNoNHY0SDN6bTE0IDBoNHY0aC00em0tNyAwaDR2NGgtNHptLTcgN2g0djRIM3ptMTQgMGg0djRoLTR6bS03IDBoNHY0aC00em0tNyA3aDR2NEgzem0xNCAwaDR2NGgtNHptLTcgMGg0djRoLTR6JyBmaWxsPScjQTdBQUIyJy8+PC9zdmc+<span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span><br /><span class="highlight-line"></span><br /><span class="highlight-line"><span class="token selector">.bento input</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">opacity</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">pointer-events</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">left</span><span class="token punctuation">:</span> -9999px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">top</span><span class="token punctuation">:</span> -9999px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-10">Issues and how to fix them</h3>
<ol>
<li>A checkbox is not a <code><button></code>.</li>
<li>The default key events on a checkbox and a button are not the same.</li>
<li>If youāre not sure when to use <code><a></code> or <code><button></code>, watch <a href="https://www.youtube.com/watch?v=8XjwDq9zG4I">The Links vs. Buttons Showdown</a> by <a href="https://twitter.com/marcysutton">Marcy Sutton</a>.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>This ābuttonā has no label.</li>
<li>Screen readers may announce: āunticked tickboxā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-12-the-bento-list-anchor-link-button">Pattern 12: The bento list anchor link button</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>offcanvas-icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>menu-button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>dot1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>dot2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>dot3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>dot4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>dot5<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>dot6<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>dot7<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>dot8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>dot9<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-11">Issues and how to fix them</h3>
<ol>
<li>Use <code><ul></code> to group and list related items, not as a button wrapper.</li>
<li>An anchor link is not a <code><button></code>.</li>
<li>CSS is quite powerful, you don't need 9 <code>span</code>s to display 9 dots.</li>
<li>If youāre not sure when to use <code><a></code> or <code><button></code>, watch <a href="https://www.youtube.com/watch?v=8XjwDq9zG4I">The Links vs. Buttons Showdown</a> by <a href="https://twitter.com/marcysutton">Marcy Sutton</a>.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>This ābuttonā has no label.</li>
<li>Screen readers may announce: ālist 1 itemā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-13-the-meatball-link">Pattern 13: The meatball link</h2>
<style>
.meatball {
text-decoration: none;
}
</style>
<p><a href="https://example.com/" class="meatball">⢠⢠ā¢</a></p>
<p>(Note: There was a click event on the link that prevented default.)</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>⢠⢠ā¢<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-12">Issues and how to fix them</h3>
<ol>
<li>A link is not a <code><button></code>.</li>
<li>The default key events on a link and a button are not the same.</li>
<li>If youāre not sure when to use <code><a></code> or <code><button></code>, watch <a href="https://www.youtube.com/watch?v=8XjwDq9zG4I">The Links vs. Buttons Showdown</a> by <a href="https://twitter.com/marcysutton">Marcy Sutton</a>.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>This ābuttonā has no label.</li>
<li>Screen readers may announce: ālink, bullet bullet bulletā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-14-the-kebab-div-button">Pattern 14: The kebab div button</h2>
<style>
.kebab {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 30px;
height: 38px;
cursor: pointer;
}
.kebab i {
background: #737473;
height: 4px;
width: 4px;
margin: 4px auto;
border-radius: 5px;
display: block;
}
</style>
<p class="kebab" title="Menu">
<i></i>
<i></i>
<i></i>
</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>menu<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Menu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-13">Issues and how to fix them</h3>
<ol>
<li>Pretty much the same as <a href="https://htmhell.dev/26-tasty-buttons/#pattern-1-the-unsemantic-burger">Pattern 1: The unsemantic burger</a>.</li>
<li>The <code>i</code> element represents a span of text in an alternate voice or mood, or otherwise offset from the normal prose in a manner indicating a different quality of text.<sup><a href="https://htmhell.dev/26-tasty-buttons/#resources"><span class="u-hidden">Footnote</span>2</a></sup> If you just want italic text, use <code>font-style: italic;</code> in CSS.</li>
<li>Screen readers may announce: nothing.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-15-the-hamburger">Pattern 15: The hamburger</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</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>nav__toggle-button<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>Hamburger Menu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>24<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>24<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 24 24<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M0 0h24v24H0z<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>none<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>path</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>currentColor<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>path</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-14">Issues and how to fix them</h3>
<ol>
<li>Not as bad as all the others, but the term āHamburgerā might confuse users.</li>
<li>The ābuttonā should indicate whether the element it controls is currently expanded or collapsed (<code>aria-expanded="false"</code> if collapsed).</li>
<li>Screen readers may announce: ābutton, Hamburger Menuā.</li>
</ol>
</div>
<div class="section">
<h2 id="pattern-16-the-chatty-burger-button">Pattern 16: The chatty burger button</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>All Company expand to see list of Company products and services<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</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><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<h3 id="issues-and-how-to-fix-them-15">Issues and how to fix them</h3>
<ol>
<li>Not as bad as all the others, but the label is too long. Keep it short and simple.</li>
<li>Screen readers may announce: ābutton, All Company expand to see list of Company products and services, expandedā.</li>
</ol>
</div>
<div class="section">
<h2 id="alternatives">Alternatives</h2>
<h3 id="solution-1-a-button-with-visible-text-and-no-icon">Solution 1: A button with visible text and no icon.</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</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></span><br /><span class="highlight-line"> Menu</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p><button aria-expanded="false" type="button">Menu</button></p>
<ol>
<li>Text only: easy to implement and comprehensible.</li>
<li>Screen readers may announce: Menu, buttonā.</li>
</ol>
</div>
<div class="section">
<h3 id="solution-2-a-button-with-visible-text-and-only-visually-accessible-icon">Solution 2: A button with visible text and only visually accessible icon.</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Menu</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p><button aria-expanded="false" type="button" style="align-items: center; gap: 10px; display: flex;"><svg aria-hidden="true" height="24" viewBox="0 0 16 16" version="1.1" width="24"><path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z" fill="currentColor"></path></svg>Menu</button></p>
<ol>
<li>If you want to use an icon, hide it from screen readers by wrapping it in a <code>span</code> with <code>aria-hidden="true"</code>.</li>
<li>Screen readers may announce: Menu, buttonā.</li>
</ol>
</div>
<div class="section">
<h3 id="solution-3-a-button-with-hidden-text-and-only-visually-accessible-icon">Solution 3: A button with hidden text and only visually accessible icon.</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Menu<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span></span><br /><span class="highlight-line"> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>50%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br /><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> -1px<span class="token punctuation">;</span></span><br /><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p><button aria-expanded="false" type="button"><span class="u-hidden">Menu</span><svg aria-hidden="true" height="24" viewBox="0 0 16 16" version="1.1" width="24"><path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z" fill="currentColor"></path></svg></button></p>
<ol>
<li>Unfortunately, thereās no native way of hiding content only visually.<br />The <code>.sr-only</code> class makes sure that content is visually hidden but still accessible to screen reader users.</li>
<li>Screen readers may announce: Menu, buttonā.</li>
</ol>
</div>
<div class="section">
<h3 id="solution-4-a-button-with-accessible-text-and-only-visually-accessible-icon">Solution 4: A button with accessible text and only visually accessible icon.</h3>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<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>Menu<span class="token punctuation">"</span></span> <span class="token attr-name">aria-expanded</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ⦠<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
<p><button aria-expanded="false" type="button" aria-label="Menu"><svg aria-hidden="true" height="24" viewBox="0 0 16 16" version="1.1" width="24"><path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z" fill="currentColor"></path></svg></button></p>
<ol>
<li>If you donāt want to show text on screen, provide a text alternative for your icon or SVG by adding <code>aria-label</code> to the button.</li>
<li>Screen readers may announce: Menu, buttonā.</li>
</ol>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ol>
<li><a href="https://tink.uk/accessibility-support-for-css-generated-content/">Accessibility support for CSS generated content</a></li>
<li><a href="https://html.spec.whatwg.org/#the-a-element">The a element</a></li>
<li><a href="https://www.scottohara.me/note/2019/07/17/placeholder-link.html">The accessibility of placeholder links </a></li>
</ol>
</div>
<div class="section">
<h2 id="wanna-learn-accessibility-testing?">Wanna learn accessibility testing?</h2>
<p>Are you interested in learning how to discover these accessibility issues and a lot more? Then join me for a <a href="https://smashingconf.com/online-workshops/workshops/manuel-matuzovic/">Smashing Magazine workshop</a> starting on November 4th.</p>
<p><strong>Deep Dive On Accessibility Testing</strong><br />
Workshop, 5Ć2.5h + Q&A<br />
Thu & Fri, November 4ā18 202109:00 ā 11:30 AM PT (Pacific, US) ⢠18:00 ā 20:30 CET (Europe)</p>
<p>Use this link for a <a href="https://ti.to/smashingmagazine/online-workshops/discount/welcometomyworkshop">special -15% discount</a>.</p>
</div>
#27 <a6>
2021-11-21T21:00:00Z
https://htmhell.dev/27-a6/
<div class="section">
Context: Visually a list of links.
</div>
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h6</span><span class="token punctuation">></span></span>Popular Cities<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h6</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h6</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>footerLinks<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Amsterdam<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h6</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h6</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>footerLinks<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Rotterdam<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h6</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h6</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>footerLinks<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Utrecht<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h6</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h6</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>footerLinks<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Den Haag<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h6</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h6</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>footerLinks<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Eindhoven<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h6</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>Use headings (<code><h1></code>-<code><h6></code>) to structure the page, donāt use heading elements arbitrarily or only to comply with visual requirements.</li>
<li>By default, a click event on an <code><h6></code> triggers only on click. A click event on an <code>a</code> triggers on click and if the user presses the <kbd>Enter</kbd> key.</li>
<li>An <code><h6></code> isnāt keyboard focusable.</li>
<li>If your content it so comprehensive and deeply structured that you need levels as deep as <code><h5></code> and <code><h6></code> in order to describe the structure, consider splitting your large page into multiple smaller pages.</li>
<li>The <code><div></code> element is an element of last resort, for when no other element is suitable. Use of the <code><div></code> element instead of more appropriate elements leads to poor accessibility.</li>
<li>If youāre listing items that thematically belong together, consider grouping them semantically using <code><ol></code> or <code><ul></code>.</li>
<li><code><a></code> allows users to right click and copy link / open in new tab.</li>
<li>Linking to pages using the <code><a></code> element works without JavaScript.</li>
<li>An <code><a></code> has the right cursor without having to add CSS.</li>
</ol>
<p>Check out the <a href="https://htmhell.dev/27-a6/#resources">resources section</a> at the bottom of this page for more.</p>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>Popular Cities<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/amsterdam.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Amsterdam<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/rotterdam.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Rotterdam<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/utrecht.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Utrecht<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/denhaag.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Den Haag<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/eindhoven.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Eindhoven<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=YG9WQUfH7ZU">Hidde de Vries - Semantics and How to Get Them Right - beyond tellerrand Düsseldorf 2021</a></li>
</ul>
</div>
#28 alert level 1
2021-11-28T21:00:00Z
https://htmhell.dev/28-alert-level-1/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">aria-busy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<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>alert<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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Done</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ul>
<li>The element is used for communicating status updates, not to structure the page. A <code>div</code> with a <code>role</code> of <code>status</code> or <code>alert</code> is more suitable than a <code>h1</code>.</li>
<li>The heading is semantically not a <code>heading</code> anymore, but an <code>alert</code> container. This can be confusing, NVDA, for example, announces āalert busy Done level 1ā. Do not change native semantics, unless you really have to.</li>
<li><code>aria-live="polite"</code> turns the element explicitly into a <em>polite</em> live region. This behavior is overwritten by <code>role="alert"</code> which turns it implicitly into an <em>assertive</em> live region.</li>
<li>For frequent updates it might be better to use a polite (<code>role="status"</code>) and a not an assertive (<code>role="alert"</code>) live region.</li>
<li><code>aria-busy</code> indicates whether an element, and its subtree, are currently being updated. The text of the live region āDoneā indicates that all the necessary updates have finished. If that's the case, <code>aria-busy</code> should be removed or set to <code>false</code>.</li>
<li>āDoneā might not be descriptive enough, consider a brief but more informative status message, something like āChanges savedā or āProduct added to cartā.</li>
<li>The heading/live region is visually hidden. Consider showing it because everyone might benefit from the information.</li>
</ul>
<p>Check out the <a href="https://htmhell.dev/28-alert-level-1/#resources">resources section</a> at the bottom of this page for more.</p>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>status<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Changes saved.</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions">ARIA live regions (MDN)</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-busy">aria-busy (MDN)</a></li>
<li><a href="https://www.w3.org/TR/wai-aria/#aria-busy">aria-busy (WAI-ARIA 1.1 spec)</a></li>
<li><a href="https://www.w3.org/TR/wai-aria/#aria-live">aria-live (WAI-ARIA 1.1 spec)</a></li>
<li><a href="https://dequeuniversity.com/library/aria/liveregion-playground">Live Region Playground</a></li>
</ul>
</div>
#29 Randomly grouping content
2021-12-10T21:00:00Z
https://htmhell.dev/29-randomly-grouping-content/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>logo.svg<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>Logo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/services<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Services<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/products<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Products<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/aboutus<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Aboutus<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>aside</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Welcome to Hell<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ul>
<li>Use the <code><section></code> element only to mark up a grouping of related content, typically introduced with a heading. Learn more about sections in <a href="https://htmhell.dev/tips/the-section-element/">Issue #8 - the section element</a>.</li>
<li>Use the <code><aside></code> element only for content that is tangentially related to the main content and not for important parts of the page or site.</li>
<li>Use the <code><nav></code> elements for important navigational groupings of links. Learn more about landmarks in <a href="https://htmhell.dev/tips/landmarks/">Issue #16 - Landmarks</a>.</li>
<li>If a link contains an image and no text, the <code>alt</code> attribute of the image serves as the link text. In such a case it might make sense to use the attribute not to describe the image, but the functionality of the link.</li>
<li>You can structure the links in a <code><nav></code> by wrapping them in an <code><ul></code> or <code><ol></code>. Learn more about lists in <a href="https://htmhell.dev/tips/ol-vs-ul-vs-div/">Issue #13 - ol vs. ul vs. div</a></li>
<li>Try to avoid empty elements.</li>
<li>A document must not have more than one <code><main></code> element that does not have the <code>hidden</code> attribute specified. Having more than one visible <code><main></code> element can confuse users because screen readers donāt announce the number of <code><main></code> elements.</li>
<li>The <code><main></code> element must not appear as a descendant of the <code><section></code> or <code><aside></code> element.</li>
<li>A page or sectioning content like <code><article></code> or <code><section></code> should only contain a single <code><footer></code>.</li>
</ul>
<p>Check out the <a href="https://htmhell.dev/29-randomly-grouping-content/#resources">resources section</a> at the bottom of this page for more.</p>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>logo.svg<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>Homepage<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/services<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Services<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/products<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Products<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/aboutus<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Aboutus<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Welcome to Hell<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://adrianroselli.com/2015/09/use-only-one-main-on-a-page.html">Use Only One main on a Page</a></li>
<li><a href="https://www.scottohara.me/blog/2021/07/16/section.html">Accessibility of the section element</a></li>
</ul>
</div>
#30 Bullet ālistā
2022-01-13T00:00:00Z
https://htmhell.dev/30-bullet-list/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ⢠HTML</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ⢠CSS</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>br</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> ⢠JavaScript</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>Use <code><p></code> for paragraphs, not lists. The standard way for creating basic lists is <code><ul></code> (when the order doesn't matter) or <code><ol></code> (when the order matters), and <code><li></code> for each item.</li>
<li>The ālistā won't be announced as a list when using a screen reader.</li>
<li><code><ul></code> and <code><ol></code> provide <a href="https://htmhell.dev/tips/ol-vs-ul-vs-div/">useful semantic information</a>. What and how assistive technology announces information differs, but:
<ol>
<li>screen readers might announce it as a ālist with itemsā</li>
<li>screen readers might announce the number of items in the list, e.g. ālist with 3 itemsā</li>
<li>screen readers might announce the bullet or number of each item</li>
<li>screen readers might announce when you enter or leave a list</li>
</ol>
</li>
<li>Screen reader users may use shortcuts to jump from list to list on a page</li>
<li><code><ul></code> and <code><ol></code> provide a selector for styling in CSS</li>
<li>When copying the fake list, all the bullets will go along with it</li>
<li>You can turn the bullet into a fire emoji using <code>::marker</code> š„</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>HTML<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>JavaScript<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></span></code></pre>
</div>
#31 additional āassistanceā
2022-04-12T00:00:00Z
https://htmhell.dev/31-additional-assistance/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/contact<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>If you find that you need additional <br /><span class="highlight-line">assistance in navigating or accessing the content of this website, </span><br /><span class="highlight-line">please call our customer service toll free number 1-800-666-8654309<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>If</span><br /><span class="highlight-line">you find that you need additional assistance in navigating or accessing </span><br /><span class="highlight-line">the content of this website, please call our customer service </span><br />toll free number 1-800-666-8654309<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="highlight-line"> Contact</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/login<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>If you find that you need additional assistance in navigating or accessing the content of this website, please call our customer service toll free number 1-800-666-8654309<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>If you find that you need additional assistance in navigating or accessing the content of this website, please call our customer service toll free number 1-800-666-8654309<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Login</span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li><code>aria-label</code>, <code>aria-labelledby</code>, etc. must not be misused for dumping contact information or other unrelated content.</li>
<li>Text in a link must describe the purpose or the target of the link.</li>
<li>The visible label of an element should match the accessible name.</li>
<li>Information concerning accessibility should be on a dedicated page (e.g. an <a href="https://www.w3.org/WAI/planning/statements/">accessibility statement</a>) and/or the contact page.</li>
<li>The <code>title</code> attribute shouldn't be used for important contact information due to inconsistent browser support and lack of accessibility.</li>
<li>Redundant link text should be avoided.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/contact<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Contact</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>/login<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> Login</span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<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></li>
</ul>
</div>
#32 almost a proper close button
2022-07-18T00:00:00Z
https://htmhell.dev/32-almost-a-proper-close-button/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">display</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flex<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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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 attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 13 13<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>true<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>15px<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>15px<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Close dialog<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</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> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>path</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>Either turn the <code><svg></code> into a semantic element (<code>role="img"</code>) or exclude it from the accessibility tree (<code>aria-hidden="true"</code>). Doing both makes no sense.</li>
<li>The button has no accessible name (a text alternative). The <code>title</code> element in the <code>svg</code> would serve as the accessible name, but <code>aria-hidden="true"</code> makes the whole SVG inaccessible to assistive technology.</li>
<li>Even without <code>aria-hidden="true"</code>, some screen reader/browser combinations might not recognise the <code><title></code> element. Label the <code><svg></code> using <code>aria-labelledby</code> or <code>aria-label</code>.</li>
<li>The <code>role=button</code> attribute and value for the button is redundant. Its implicit default role is ābuttonā.</li>
<li>There's no <code>display</code> attribute. If you need custom attributes, use the <code>data-</code> prefix.</li>
<li>The <code>name</code> attribute is not allowed on <code><svg></code></li>
<li>The value of the <code>width</code> and <code>height</code> attributes should be a valid non-negative integer.</li>
<li>Add a <code>type="button"</code> attribute and value to prevent the button from submitting a form, if there is one.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span> <span class="token attr-name">data-display</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flex<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</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 attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 13 13<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>15<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>15<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#000<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</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>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Close<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</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> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>path</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="demo-of-voice-over-on-macos">Demo of Voice Over on macOS</h2>
<p>Voice Over doesn't recognize the title element nested in a svg nested in a button.</p>
<video src="https://htmhell.dev/images/svgbutton.mov" controls="" width="200">
<p>Something went wrong, please visit <a href="https://htmhell.dev/32-almost-a-proper-close-button/">htmhell.dev/32-almost-a-proper-close-button/</a> to view this video.
</p></video>
<p>Code on <a href="https://codepen.io/matuzo/pen/LYdWpwP?editors=1000">CodePen</a>.</p>
</div>
#33 make me one (input) with everything
2024-08-27T00:00:00Z
https://htmhell.dev/33-make-me-one-(input)-with-everything/
<p>The good intentions were there but in the HTML and Accessibility world, less is sometimes more.</p>
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>textinput<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>textinput<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>First name<span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>First name<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>First name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>The <code>aria-label</code>, <code>placeholder</code>, and <code>title</code> attributes all provide the same informaton ("First name"), leading to the screenreader reading the same text multiple times.</li>
<li>The <code>aria-label</code> is unnecessary since the input is already correctly labeled by the <code><label></code> element. <code><label></code> elements should be preferred to <code>aria-label</code> since they are also visible to sighted users.</li>
<li>The <code>title</code> attribute is not needed here, as the label and placeholder already convey the necessary information. Using title in this context adds unnecessary complexity.</li>
<li>The <code>placeholder</code> text should provide a hint / example to the user what kind of input is expected, it should not act as a label or contain the same content.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>textinput<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First name<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></span><br /><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>textinput<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label"><code><label></code> on MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label"><code>aria-label</code> on MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/placeholder"><code>placeholder</code> on MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title"><code>title</code> on MDN</a></li>
</ul>
</div>
#34 a button is not a link
2024-11-26T00:00:00Z
https://htmhell.dev/34-a-button-is-not-a-link/
<div class="section bad">
<h2 id="bad-code">Bad code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>button<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript">window<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'https://example.com/other-page'</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Link target description<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section" id="issues">
<h2 id="issues-and-how-to-fix-them">Issues and how to fix them</h2>
<ol>
<li>A button opening a link will be unexpected behavior for screen reader users. No matter how it is styled.</li>
<li>Links disguised as buttons wonāt show up in the link list of a site in assistive technologies.</li>
<li>Use links for navigation to other pages or sections, and buttons for actions performed on the current page or within the application.</li>
</ol>
</div>
<div class="section">
<h2 id="good-code">Good code</h2>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation"><</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>https://example.com/other-page<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Link target description<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></span></code></pre>
</div>
<div class="section">
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a"><code><a></code> on MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button"><code><button></code> on MDN</a></li>
<li><a href="https://yatil.net/blog/buttons-vs-links">Buttons vs. Links by Eric Eggert</a></li>
</ul>
</div>