build prev

This commit is contained in:
2026-03-25 13:00:47 -07:00
parent ebe2490fd3
commit 0439f0478d
282 changed files with 5212 additions and 3471 deletions

View File

@ -4,12 +4,79 @@
<subtitle>Lee Cattarin... on the internet!</subtitle>
<link href="https://leecat.art/feed.xml" rel="self" />
<link href="https://leecat.art/" />
<updated>2026-02-19T00:00:00Z</updated>
<updated>2026-03-22T00:00:00Z</updated>
<id>https://leecat.art/</id>
<author>
<name>Lee Cattarin</name>
<email>lee.cattarin@gmail.com</email>
</author>
<entry>
<title>accessible image modals</title>
<link href="https://leecat.art/accessible-image-modals/" />
<updated>2026-03-22T00:00:00Z</updated>
<id>https://leecat.art/accessible-image-modals/</id>
<content type="html">&lt;p&gt;Recently I&#39;ve been working on a single-page digital rendition of a zine complete with many hand-drawn images. The author wanted to be able to bring images up to a full-screen view, to either zoom in or to put the whole enlarged image on one screen with no scrolling. It was a real struggle to find resources on how to do this in an accessible manner, so I&#39;m writing up what I did.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fair warning: This solution is likely imperfect.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;the-dialog-element&quot;&gt;the &lt;code&gt;dialog&lt;/code&gt; element&lt;/h2&gt;
&lt;p&gt;do you know how many tutorials want you to roll your own modals? It&#39;s a not-insignificant amount. W3Schools, top of the search results in many cases, recommends it in two places - &lt;a href=&quot;https://www.w3schools.com/howto/howto_css_modal_images.asp&quot; target=&quot;_blank&quot;&gt;image modals&lt;/a&gt; and &lt;a href=&quot;https://www.w3schools.com/css/css3_images_modal.asp&quot; target=&quot;_blank&quot;&gt;responsive image modals&lt;/a&gt;. Several search results for &amp;quot;image modal&amp;quot; pop up div solutions - &lt;a href=&quot;https://stackoverflow.com/questions/75598914/how-to-display-an-image-clicking-on-it-using-modal-window-on-html-css-and-js&quot; target=&quot;_blank&quot;&gt;div modal 1&lt;/a&gt;, &lt;a href=&quot;https://dev.to/salehmubashar/create-an-image-modal-with-javascript-2lf3&quot; target=&quot;_blank&quot;&gt;div modal 2&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/watch?v=Y9TNHynFjaQ&quot; target=&quot;_blank&quot;&gt;div modal 3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you don&#39;t know of the existence of &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;, a search for image modals will not get you there quickly.&lt;/p&gt;
&lt;p&gt;If you search for &lt;em&gt;accessible&lt;/em&gt; image modals, you&#39;ll still hear about &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s. Hell, &lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/dialog/&quot; target=&quot;_blank&quot;&gt;W3C&#39;s ARIA Authoring Practices Guide uses a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;&lt;/a&gt;. You kinda have to go digging to read about the &lt;a href=&quot;https://www.scottohara.me/blog/2023/01/26/use-the-dialog-element.html&quot; target=&quot;_blank&quot;&gt;dialog element from Scott O&#39;Hara&lt;/a&gt; or find &lt;a href=&quot;https://accessibleweb.dev/modals&quot; target=&quot;_blank&quot;&gt;AccessibleWeb.dev&#39;s piece on modals&lt;/a&gt;. Or you can go &lt;a href=&quot;https://adrianroselli.com/2025/06/where-to-put-focus-when-opening-a-modal-dialog.html&quot; target=&quot;_blank&quot;&gt;straight for Adrian Roselli&lt;/a&gt; and find examples that use the native &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element. Thanks Adrian!&lt;/p&gt;
&lt;p&gt;Did I roll my own modal at first? Regretfully, yes. I&#39;d used the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element before... several years ago... in a project I don&#39;t have access to anymore... Needless to say, I had forgotten about its existence.&lt;/p&gt;
&lt;p&gt;Anyway, I got there. Eventually. So let&#39;s talk about modals and &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;what-does-dialog-give-us&quot;&gt;what does &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; give us?&lt;/h3&gt;
&lt;p&gt;so, what do we get from the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element that we don&#39;t get from a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; modal?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;a semantically meaningful element&lt;/li&gt;
&lt;li&gt;a backdrop that fills the screen behind the modal, styleable with &lt;code&gt;::backdrop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;automatic use of the &lt;code&gt;Esc&lt;/code&gt; key to close the modal&lt;/li&gt;
&lt;li&gt;automatic focus trapping that prevents any tabbing within the page behind the modal (users can tab off the page into the browser buttons)&lt;/li&gt;
&lt;li&gt;the JS functions &lt;code&gt;.showModal()&lt;/code&gt; and &lt;code&gt;.close()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;closedby&lt;/code&gt; is set to &lt;code&gt;any&lt;/code&gt;, clicking outside the modal (anywhere on the backdrop) will also close the modal&lt;/li&gt;
&lt;li&gt;automatic return of focus to the element that triggered the modal&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;autofocus&lt;/code&gt; attribute for in-modal elements to set which element should receive focus on opening the modal&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;open&lt;/code&gt; attribute which is set on the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; when open and removed when closed (when you use the functions mentioned previously)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;and more. This is just the pieces in use for me. There&#39;s also things like &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#additional_notes&quot; target=&quot;_blank&quot;&gt;setting forms so that submission closes the dialog&lt;/a&gt; (point 1 in that list).&lt;/p&gt;
&lt;h3 id=&quot;what-doesnt-it-give-us&quot;&gt;what doesn&#39;t it give us?&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;a close button - gotta roll your own and attach the requisite &lt;code&gt;.close()&lt;/code&gt; call (or rely on users hitting escape or clicking the backdrop)&lt;/li&gt;
&lt;li&gt;prevention of scroll on the rest of the page&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;a-bug&quot;&gt;a bug&lt;/h3&gt;
&lt;p&gt;MDN warns:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do not add the &lt;code&gt;tabindex&lt;/code&gt; property to the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element as it is not interactive and does not receive focus.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#additional_notes&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;: additional notes&lt;/a&gt; (point 3 in that list).&lt;/p&gt;
&lt;p&gt;despite this, I found that in Firefox (but not Edge), the &lt;em&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; itself was focusable&lt;/em&gt;. I have no idea why, but I tested this on a totally unstyled and unmodified page and still found it to be true. As a focusable element, it made no sense. It had no interactivity and could not be activated.&lt;/p&gt;
&lt;p&gt;I&#39;m still torn: do I add &lt;code&gt;tabindex=&amp;quot;-1&amp;quot;&lt;/code&gt;? MDN specifically says not to, but I&#39;m pretty sure they&#39;re warning against making it &lt;em&gt;focusable&lt;/em&gt;. Why warn against making it nonfocusable when &lt;em&gt;it&#39;s not supposed to be focusable in the first place&lt;/em&gt;, after all?&lt;/p&gt;
&lt;p&gt;At current, I&#39;m ambivalent, but I&#39;ve added &lt;code&gt;tabindex=&amp;quot;-1&amp;quot;&lt;/code&gt; to handle Firefox&#39;s poor behavior. Making the dialog focusable is unhelpful and confusing.&lt;/p&gt;
&lt;h2 id=&quot;in-addition-to-the-dialog&quot;&gt;in addition to the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;here&#39;s what else I wrote...&lt;/p&gt;
&lt;h3 id=&quot;html&quot;&gt;html&lt;/h3&gt;
&lt;p&gt;besides the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;, I gave my &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements &lt;code&gt;tabindex=&amp;quot;0&amp;quot;&lt;/code&gt; to make them focusable.&lt;/p&gt;
&lt;h3 id=&quot;js&quot;&gt;js&lt;/h3&gt;
&lt;p&gt;in &lt;code&gt;modal.js&lt;/code&gt;, I created an &lt;code&gt;openDialog()&lt;/code&gt; function that takes in the clicked image. It:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;creates a new &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element and copies over the &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;alt&lt;/code&gt; attributes - importantly, it doesn&#39;t copy the full &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; because we &lt;em&gt;don&#39;t&lt;/em&gt; want to copy that &lt;code&gt;tabindex&lt;/code&gt; attribute&lt;/li&gt;
&lt;li&gt;replaces the current &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; in the modal with our new copy with &lt;code&gt;replaceChild()&lt;/code&gt; (or if there&#39;s no current one, it just appends)&lt;/li&gt;
&lt;li&gt;calls &lt;code&gt;dialog.showModal()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I gave my close button two event listeners, one that listens for click events and one that listens for a keydown of the space or enter keys. In the case of the keydown, it calls &lt;code&gt;event.preventDefault()&lt;/code&gt; to stop the space key from scrolling the underlying page.&lt;/p&gt;
&lt;p&gt;I also looped through all images and attached my &lt;code&gt;openDialog()&lt;/code&gt; function to any image with a &lt;code&gt;tabindex&lt;/code&gt; attribute (I had some images that weren&#39;t intended to be fullscreened, so they lacked &lt;code&gt;tabindex&lt;/code&gt;). Again, I gave them listeners on both click and keydown.&lt;/p&gt;
&lt;h3 id=&quot;css&quot;&gt;css&lt;/h3&gt;
&lt;p&gt;here&#39;s the most relevant parts of the CSS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dialog::backdrop&lt;/code&gt; was given a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Colors/Using_relative_colors&quot; target=&quot;_blank&quot;&gt;relatively calculated&lt;/a&gt; color - &lt;code&gt;rgba(from var(--color-bg) r g b / .8)&lt;/code&gt; - as well as a blur&lt;/li&gt;
&lt;li&gt;&lt;code&gt;body:has(dialog[open])&lt;/code&gt; has &lt;code&gt;overflow: hidden&lt;/code&gt; set&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dialog img&lt;/code&gt; uses a &lt;code&gt;max-height&lt;/code&gt; as well as &lt;code&gt;object-fit: contain&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;errors-questions&quot;&gt;errors? questions?&lt;/h2&gt;
&lt;p&gt;reach out!&lt;/p&gt;
</content>
</entry>
<entry>
<title>eleventy lessons</title>
<link href="https://leecat.art/eleventy-lessons/" />
@ -420,14 +487,6 @@ eleventyExcludeFromCollections: true
<updated>2026-01-18T00:00:00Z</updated>
<id>https://leecat.art/fire-and-ice-handspun/</id>
<content type="html">&lt;p&gt;Fiber from &lt;a href=&quot;https://www.etsy.com/shop/JakiraFarms&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Jakira Farms&lt;/a&gt; in Fire &amp;amp; Ice colorway. 100% merino.&lt;/p&gt;
</content>
</entry>
<entry>
<title>dyeing fiber</title>
<link href="https://leecat.art/dyeing-fiber/" />
<updated>2026-01-18T00:00:00Z</updated>
<id>https://leecat.art/dyeing-fiber/</id>
<content type="html">&lt;p&gt;hand-dyed with acid dyes&lt;/p&gt;
</content>
</entry>
</feed>