Adrian Roselli https://adrianroselli.com Consultant, Writer, Speaker Thu, 05 Mar 2026 14:13:59 +0000 en-US hourly 1 https://wordpress.org/?v=5.9.13 Your Browser Can Already Speak a Page https://adrianroselli.com/2026/03/your-browser-can-already-speak-a-page.html https://adrianroselli.com/2026/03/your-browser-can-already-speak-a-page.html#respond Thu, 05 Mar 2026 14:13:59 +0000 https://adrianroselli.com/?p=48866 “Read aloud” controls on a web page showing back, forward, and play buttons, a slider to control speaking speed, and a select menu to choose voice, with Microsoft Mark selected.
Users can customize the features built into the browser, something not often available from third-party approaches.

Is an “AI” company offering to provide spoken versions of your pages for users? Is an overlay company promising to make your content more accessible by its overlay speaking it? Is some other vendor pitching you on some kind of thing that reads your web pages aloud to users?

You don’t need it.

Or, rather, your users may not need it. Their browser already offers it. At no cost to you. Nor them.

No Really, Try It

These features may not appear for pages that are not well-formed (lack a main region, are nothing but div-soup, etc.). If they don’t appear for your content, that’s a signal that you may have work to do on the underlying HTML. Any fixes you make would likely result in a better experience for everyone.

Instructions for common browsers:

  • In Firefox on desktop, you can enter reader view (F9) and then activate the headphone / “Read aloud” button (N).
  • Chrome:
    • in Chrome desktop, right-click, choose “Open in reading mode”, and then hit the play button (K).
    • in Chrome mobile, activate the “More” kebab, then choose “Listen to this page.”
  • Edge:
    • Edge on desktop offers the Read Aloud feature (Ctrl + Shift + U).
    • Edge on mobile uses the immersive reader, where you can choose “Read aloud” from the toolbar.
  • Safari:
    • on iDevice Safari, activate the “Page menu” button (the double-underlined rectangle on the left of the address bar) and choose “Listen to Page.”
    • on macOS Safari, choose “Edit” from the menu bar, then “Speech,” then “Start Speaking.”

In most cases, users can choose the reading speed and voice, pause or restart it, and even jump back and forth through paragraphs. This is more control than third-party products offer.

But Also

If an “AI” company offered on-demand or pre-recorded audio, they’re leveling up our overall destruction of the environment.

If an overlay vendor offered it through its overlay, you’re adding legal risk, trusting them to not be liars, letting them track your users, and assuming it will work. Because, as I’ve documented extensively, overlays don’t work (sometimes at all but usually as advertised).

Any other vendor is probably a mix of data harvester, crypto miner, scam platform, or government panopticon node.

Users who most benefit from a page being spoken to them know about browsers’ built-in features. Those users may rely on extensions for even more control. They are unlikely to benefit from (or care about) these third-party offerings.

However

If you have good research, not provided by the vendor, that your specific users are asking for this specific feature then sure. Go for it. But also track its use and understand you’re taking on the vendor’s risks as your own — for those users whose blockers don’t block it.

]]>
https://adrianroselli.com/2026/03/your-browser-can-already-speak-a-page.html/feed 0
Honoring Mobile OS Text Size https://adrianroselli.com/2026/02/honoring-mobile-os-text-size.html https://adrianroselli.com/2026/02/honoring-mobile-os-text-size.html#comments Fri, 06 Feb 2026 20:35:57 +0000 https://adrianroselli.com/?p=48799 If your users scale the text size in Android or iDeviceOS, that doesn’t always affect the size of text on a web page. It’s a function of browser and authored code, as opposed to a standardized approach. That may be changing.

Support

The current state of affairs in the three rendering engines…

Firefox

Firefox on Android supports scaling a web page’s text if you adjust the system font size. It doesn’t seem to care if you use px units. You don’t need to do anything special; it just works.

Safari

Safari will not scale a web page’s text with the OS settings unless the author adds some CSS. This code points to the system font properties (such as size):

body {
  font: -apple-system-body;
  /* other styles, including font-family */
}

This feature query checks for support while trying to limit it to touch devices only, then sets the font size:

@supports (font: -apple-system-body) and (not (-webkit-touch-callout: default)) {
  :root {
    font-size: 100%;
  }
}

The rest of your styles have to use relative units or this breaks down.

Chrome

Google has opted not to follow Firefox’s lead and support OS text sizes by default. Google has also opted not to mimic Safari and make platform-specific features. Instead, Google has decided to use its weight in the CSSWG to mint a new value for the <meta> element:

<meta name="text-scale" content="scale">

For this to work, however, the author will need to “opt-in” by not setting a base font size in your CSS:

body {
  /* font-size: yeah, no */
}

You actually can set one, but it should be a percentage. You then need to avoid fixed units (such as px) throughout your site. If this seems familiar, it’s the core thesis of my aptly-named post The Ultimate Ideal Bestest Base Font Size That Everyone Is Keeping a Secret, Especially Chet.

Read Josh Tumath’s post Try text scaling support in Chrome Canary for more details.

Frankenstyle’s Monster

Let’s combine those into one set of code that will work in Safari and now Chrome. Firefox was always on point, so no effort needed there.

Add this HTML to the <head> of your page (it’s the CSSWG code):

<meta name="text-scale" content="scale">

Add this to the CSS of your page (it’s Apple’s code):

body {
  font: -apple-system-body;
  /* other styles, including font-family */
}
@supports (font: -apple-system-body) and (not (-webkit-touch-callout: default)) {
  :root {
    font-size: 100%;
  }
}

Done.

Frankenstyle’s Demo

I made a demo. It has images and a table. Visit the debug version in your mobile browser.

See the Pen Untitled by Adrian Roselli (@aardrian) on CodePen.

How to Test Frankenstyle’s Demo

Seriously, visit the debug version in your mobile browser. I’m not dealing with desktop here.

With your Android device, install Chrome Canary from the Google Play store. If you are using an Android device with another app store, I leave it to you to sort that out. Then, using chrome://flags, enable Experimental Web Platform Features in Chrome Canary.

Android

In Android, go to SettingsAccessibilityDisplay size & text; in the Size options section, go to Font size and make it as large as possible.

The Android text size settings screen at the smallest size. The Android text size settings screen at the largest size.
The smallest Android text size setting and then the largest setting.

iDevices

In iDevices, go to SettingsAccessibilityDisplay & Text SizeLarger Text, toggle Larger Accessibility Sizes, then drag that slider all the way to the to max.

The iPadOS text size settings screen at the smallest size. The iPadOS text size settings screen at the largest size.
The smallest iPadOS text size setting and then the largest setting.

My Results from Frankenstyle’s Demo

These are before (small text) and after (largest text) images of Frankenstyle’s Demo. Your experience may differ. If you’re reading this from the future, it may differ a lot.

None of these required a browser reload. It may take a moment, so be patient.

Firefox

Firefox showing four paragraphs and the embedded photo of Jeff Goldblum. Firefox showing twelve lines of text under the massive heading and about 20 characters per line.
Frankenstyle’s Demo in Firefox on Android, behaving just as it would without the Frankenstyles.

Safari

Safari showing about four paragraphs of text and images. Safari showing eight lines of text and about 10 characters per line.
Frankenstyle’s Demo in Safari on iPadOS. I scrolled the second one a bit to show the inline images.

Chrome Canary

Chrome Canary showing four paragraphs and the embedded photo of Jeff Goldblum. Chrome Canary showing twelve lines of text under the massive heading and about 20 characters per line.
Chrome Canary showing Frankenstyle’s Demo.

Other Reading

Things that may better inform those words and pictures above:

]]>
https://adrianroselli.com/2026/02/honoring-mobile-os-text-size.html/feed 2
You Know What? Just Don’t Split Words into Letters https://adrianroselli.com/2026/02/you-know-what-just-dont-split-words-into-letters.html https://adrianroselli.com/2026/02/you-know-what-just-dont-split-words-into-letters.html#comments Thu, 05 Feb 2026 17:45:05 +0000 https://adrianroselli.com/?p=48732 This is an unplanned part two for Barriers from Links with ARIA. The title reflects my exasperation because this isn’t new, I’ve simply failed to be explicit about it over the last decade or so.

In 2012 I vented about TypeButter using <kern style="letter-spacing: -0.01em;"> for each letter. In 2020 I noted the AWWWards site wrapped every letter in a <div> for animation, which screen readers presented letter by letter. In 2022, it was BeeLine Reader using <span>s to achieve gradients across a word.

In 2026 I am finally writing about it because GSAP has its SplitText plug-in asserting screen reader support that doesn’t stand up to use. I appreciate the embedded video explains how it should work, and even includes an unnamed screen reader demo, but GSAP assumes authors will use SplitText in a very specific way and fails to note potential problems. That’s a bummer because now I have to be the buzzkill.

GSAP’s Demo

This is the animation demo on the SplitText page (or go to the debug view if you want to test it):

See the Pen SplitText Demo by GSAP (@GreenSock) on CodePen.

I also made a fork, just in case GSAP changes the code later (which would mess with future testing).

The simplest way to test this is to fire up a screen reader and try navigating the page with whatever method you prefer. You may get different results than I did, but that’s the fun of testing! Though I did get confirmation from one, then another, then a third screen reader user.

If you press the “Characters” button, this is the HTML output of the first line of text (which is in its own <div>):

<div aria-hidden="true" style="position: relative; display: block; text-align: center;">
  <div aria-hidden="true" style="position: relative; display: inline-block;">
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">B</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">r</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">e</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">a</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">k</div>
  </div>
  <div aria-hidden="true" style="position: relative; display: inline-block;">
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">a</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">p</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">a</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">r</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">t</div>
  </div>
  <div aria-hidden="true" style="position: relative; display: inline-block;">
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">H</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">T</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">M</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">L</div>
  </div>
  <div aria-hidden="true" style="position: relative; display: inline-block;">
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">t</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">e</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">x</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">t</div>
  </div>
  <div aria-hidden="true" style="position: relative; display: inline-block;">
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">i</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">n</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">t</div>
    <div aria-hidden="true" style="position: relative; display: inline-block; translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px);">o</div>
  </div>
</div>

Five words.

The ARIA Non-Solution

The demo uses a <div>, which in the HTML AAM maps to the generic role. The problem here is that the generic role does not allow itself to be named by the author — which means aria-label is prohibited on it.

The example code GSAP offers, however, shows headings instead (which allow aria-label). It makes no mention of the restrictions on which roles allow aria-label. An author might think nothing of using aria-label on a <div>. Kind of like the embedded GSAP demo.

When aria-label is used on an element that allows it, the GSAP page makes no mention of other risks. I’ve repeatedly said aria-label may not auto-translate for users. It also doesn’t talk about WCAG SC 2.5.3 Label in Name risks when aria-label is applied to any control.

Hiding Content

Kind of a nitpick, but the GSAP page links to a CSS-Tricks post that itself simply links to Scott’s Inclusively Hidden post. Scott’s post got an update in 2023. CSS-Tricks’ post did not. I consider that a disservice to both Scott and readers.

Videos

I hate making these videos. It takes so much time. But it’s evidence. Or proof. Or something.

NVDA 2025.3.2 with Firefox 147.0.2. The text is announced both before and after animation.
JAWS 2026.2512.50 with Chrome 144. The text is never announced.
Narrator Win 11 25H2 26200.7623 with Edge 144. Only the first letter of the text is ever announced.
VoiceOver macOS 26.2 with Safari 26.2. Only a sub-set of the letters is exposed, and those are read letter-by-letter. This video is more sloppy owing to not feeding the audio properly and an electrician poking holes in my walls.
Orca Ubuntu 25.04 with Firefox 146. The text is announced both before and after animation.

I was going to make videos for VoiceOver iPadOS with Safari, TalkBack with Chrome, and TalkBack with Firefox, but I got tired. If you’ve been reading my blog long enough, then you should know the commands to give it a try.

Similarly, you can pop open the Braille viewers on the desktop and see how those perform.

Results

I’ve reduced these to a binary yes/no. I also added mobile results, for which I made no videos.

SplitText in Screen Readers
Pairing Works?
NVDA / Firefox yes
JAWS / Chrome no
Narrator / Edge no
VO macOS / Safari no
Orca / Firefox no
TalkBack / Chrome yes
TalkBack / Firefox no
VO iPadOS / Safari no

Bug Report

This post is a warning to authors. I’ve also filed an issue with GSAP asking the SplitText page to clarify the risks and limitations: #642 Screen Readers do not expose SplitText

I don’t expect them to be able to jump on the bug report immediately, and they will almost definitely want to perform further testing. So until they can tackle it, I strongly recommend avoiding SplitText.

Wrap-up

If you need to split words into their constituent letters in order to adjust kerning, give them gradients, animate them, or whatever, well, no you don’t. Find another method.

If the GSAP people who guaranteed their approach works with screen readers got it wrong, it seems likely you will too. Unless you have all the screen reader, browser, platform, and TTS variations along with the screen reader navigation skills needed to perform ongoing and robust testing.

Which you don’t.

Update: Same Damn Day

Turn your terminal into is completed letter by letter first with an interface designer, which is deleted letter by letter and replaced with a frontend wizard, and then replaced by an accessibility expert. The browser dev tools show it’s all in a <span> with an aria-label.

The makers of Tailwind, dissatisfied with pushing verbose class names on human authors only, have decided to target LLMs with ui.sh (pronounced “wish”). While this hilarious, non-parody home page doesn’t make the mistake of wrapping every letter in its own element, it does make the mistake of using aria-label.

I’ve decided it warrants a mention here.

The good news is this will help guarantee the need for human accessibility practitioners over poorly-trained LLMs.

Update: 10 February 2026

Jeffrey Yasskin says the W3C TAG was looking at a proposal for <canvas> to split a JavaScript string into glyphs to render them in the canvas, with the TAG asking for the proposal to squeeze those “letter” into the DOM. I say “was” because as of yesterday the TAG closed it as “unsatisfied.” Read the TAG’s (via Matt) reasoning for why. Also, Jeffrey added more context on Masto.

]]>
https://adrianroselli.com/2026/02/you-know-what-just-dont-split-words-into-letters.html/feed 2
Barriers from Links with ARIA https://adrianroselli.com/2026/01/barriers-from-links-with-aria.html https://adrianroselli.com/2026/01/barriers-from-links-with-aria.html#comments Fri, 23 Jan 2026 01:53:22 +0000 https://adrianroselli.com/?p=48694 Today Temani Afif asked a question:

I have my canned response that aria-label auto-translation is inconsistent.

But the something else maybe question is what reminded me that this construct has caused issues outside of WCAG concerns. In particular, the only assistive technologies (AT) that consume ARIA are screen readers and, to a far lesser extent, voice control. That latter part only because browsers assemble the accessible names, not AT. There’s plenty more AT that never touches ARIA.

I knew there were issues, but couldn’t rattle them off from the top of my head. So I built some examples and poked them with other accessibility features of browsers.

Results

I am not testing screen readers nor voice control.

  • The text with each letter in its own span does not auto-translate..
  • Edge’s Read Aloud feature (Ctrl + Shift + U) does not announce the aria-label value of any link.
  • Edge’s Read Aloud feature does not announce the links with aria-hidden, regardless of other attributes.
  • Chrome’s reader mode text-to-speech does not announce the aria-label value of any link.
  • Chrome’s reader mode text-to-speech does not announce the links with aria-hidden, regardless of other attributes.
  • Chrome’s reader mode visually hides every link with aria-hidden.
  • Firefox’s reader mode visually hides every link with aria-hidden.
  • Firefox’s reader mode visually styled every link that spanned its letters as white instead of blue or purple.
  • Safari’s Speech feature jumps past most of the page when it gets to the first instance of letters split across spans, and announces subsequent links with the wrong link text (using the Korean text for English links).
  • Safari’s Speech feature does not announce the aria-label value of any link.
  • Safari’s Speech feature does not announce the span-separated letters nor visible word they make up.
  • Chrome and Edge would not let me link to the highlighted text when I highlighted the aria-hidden links, though that changed when I got more content on the page.

As Amelia Bellamy-Royds points out, the text-to-speech features seem to respect the aria-hidden but ignore the aria-label — and by extension the accessible name the browser provides in the accessibility tree.

Takeaways

Three key takeaways here (yes, I know your use case is special):

  1. Don’t use aria-label on links;
  2. Don’t use aria-hidden within links;
  3. Don’t split the letters of a word across elements.

I look forward to you, dear reader, trying other approaches and letting me know where these fall down (or what I got wrong). I don’t need to know where they are supported. Just the gaps.

Tests

These are the tests I used to generate the results you just read (originally as a Codepen). You can ignore this part unless you want to run your own tests. If you want to find other ways these approaches might break for users, then please do so.

I used the Korean words 샌드위치 for “sandwich” and 망치 for “hammer” (the values of aria-label).

The links with aria-label all fail WCAG SC 2.5.3 Label in Name, but it’s intentional so I can quickly tell if the aria-label is exposed. Similarly, the links with aria-hidden and no aria-label fail 4.1.2 Name, Role, Value, but again I wanted to see how they performed.

Standard link: Sandwich
<a href="proxy.php?url=https://en.wikipedia.org/wiki/Sandwich">
  Sandwich
</a>
Standard link in another language: 샌드위치
<a href="proxy.php?url=https://ko.wikipedia.org/wiki/%EC%83%8C%EB%93%9C%EC%9C%84%EC%B9%98" hreflang="ko" lang="ko">
  샌드위치
</a>
Link with aria-label: Sandwich
<a href="proxy.php?url=https://en.wikipedia.org/wiki/Sandwich" aria-label="hammer">
  Sandwich
</a>
Link in another language with aria-label: 샌드위치
<a href="proxy.php?url=https://ko.wikipedia.org/wiki/%EC%83%8C%EB%93%9C%EC%9C%84%EC%B9%98" hreflang="ko" lang="ko" aria-label="망치">
  샌드위치
</a>
Link with each letter of visible text in its own <span>: Sandwich
<a href="proxy.php?url=https://en.wikipedia.org/wiki/Sandwich">
  <span>
    <span>S</span><span>a</span><span>n</span><span>d</span><span>w</span><span>i</span><span>c</span><span>h</span>
  </span>
</a>
Link in another language with each letter of visible text in its own <span>:
<a href="proxy.php?url=https://ko.wikipedia.org/wiki/%EC%83%8C%EB%93%9C%EC%9C%84%EC%B9%98" hreflang="ko" lang="ko">
  <span>
    <span>샌</span><span>드</span><span>위</span><span>치</span>
  </span>
</a>
Link with aria-label and each letter of visible text in its own <span>: Sandwich
<a href="proxy.php?url=https://en.wikipedia.org/wiki/Sandwich" aria-label="hammer">
  <span>
    <span>S</span><span>a</span><span>n</span><span>d</span><span>w</span><span>i</span><span>c</span><span>h</span>
  </span>
</a>
Link in another language with aria-label and each letter of visible text in its own <span>:
<a href="proxy.php?url=https://ko.wikipedia.org/wiki/%EC%83%8C%EB%93%9C%EC%9C%84%EC%B9%98" hreflang="ko" lang="ko" aria-label="망치">
  <span>
    <span>샌</span><span>드</span><span>위</span><span>치</span>
  </span>
</a>
Link with aria-hidden on visible text with each letter of visible text in its own <span>:
<a href="proxy.php?url=https://en.wikipedia.org/wiki/Sandwich">
  <span aria-hidden="true">
    <span>S</span><span>a</span><span>n</span><span>d</span><span>w</span><span>i</span><span>c</span><span>h</span>
  </span>
</a>
Link in another language with aria-hidden on visible text with each letter of visible text in its own <span>:
<a href="proxy.php?url=https://ko.wikipedia.org/wiki/%EC%83%8C%EB%93%9C%EC%9C%84%EC%B9%98" hreflang="ko" lang="ko">
  <span aria-hidden="true">
    <span>샌</span><span>드</span><span>위</span><span>치</span>
  </span>
</a>
Link with aria-label and aria-hidden on visible text with each letter of visible text in its own <span>:
<a href="proxy.php?url=https://en.wikipedia.org/wiki/Sandwich" aria-label="hammer">
  <span aria-hidden="true">
    <span>S</span><span>a</span><span>n</span><span>d</span><span>w</span><span>i</span><span>c</span><span>h</span>
  </span>
</a>
Link in another language with aria-label and aria-hidden on visible text with each letter of visible text in its own <span>:
<a href="proxy.php?url=https://ko.wikipedia.org/wiki/%EC%83%8C%EB%93%9C%EC%9C%84%EC%B9%98" hreflang="ko" lang="ko" aria-label="망치">
  <span aria-hidden="true">
    <span>샌</span><span>드</span><span>위</span><span>치</span>
  </span>
</a>

Don’t be shy about making your own variations and leaving your results in the comments.

Update: 5 February 2026

You know what? Just don’t split words into letters. It’s not just a problem with links, even if it’s more excitingly wrong with links.

]]>
https://adrianroselli.com/2026/01/barriers-from-links-with-aria.html/feed 6
Live Region Support https://adrianroselli.com/2026/01/live-region-support.html https://adrianroselli.com/2026/01/live-region-support.html#comments Wed, 14 Jan 2026 19:57:57 +0000 https://adrianroselli.com/?p=48638 This post does not discuss whether live regions are good, nor is it a post about the best way to use them. This post only covers how they are exposed to the audience who experiences them — screen reader users. Written by a non-screen-reader user.

1970s movie poster of darkened baby carriage with two clawed and bloody hands reaching out while cops run in the far background, titled “It’s Alive!”. If you’re here because your live region isn’t working as you expect, then your expectation is wrong, you did something wrong, or you found a fun bug. Read Pat’s Why are my live regions not working? instead of this post.

If you want a quick review of current support and simple test cases with testing steps, then keep reading. Otherwise, I dunno, close this tab?

Demo

I made this demo in mid-2024 to demonstrate a problem. It now covers five kinds of live regions:

That last one is a bit of a curve ball because it isn’t a live region, but it can functionally behave like one. Many authors are unaware of this, and for those who are aware, many think it’s a bug. The example uses a button, text input, and select menu. The button fires on activation, the two fields on change. Give the live region time to clear before moving to the next control. When testing, be sure you are hearing the de facto live region and not that you moved focus to the subsequent control with a description.

The demo also shows how these live regions work when hidden using any of three methods:

As always, I have a debug view without the Codepen cruft so you can test more easily.

See the Pen Live Regions, Hidden and Not by Adrian Roselli (@aardrian) on CodePen.

Results

The detailed results that follow the table go into specific versions and browsers used. Failure for a screen reader to support a feature properly may be a function of the browser, not the screen reader (especially with dynamic descriptions). Other bits:

  • buggy means that an implementation has bugs. These could be from the browser or the screen reader. Wade into the detailed results later in this post to check the most current version.
  • yes means the feature works as intended. You’ll note it only appears in the last column because it’s the only one that’s unambiguous in what is expected or preferred.
  • Blank cells reflect what I could not test because I had no Braille viewer.
  • I updated Chrome just before publishing and got a different result on the dynamic description test, so I threw a question mark on it until I can confirm if this is user error or some kind of change.
Current Support
Screen Reader / Browser polite, Audio polite, Braille assertive, Audio assertive, Braille alert Role, Audio alert Role, Braille alert Role, Audio “alert” alert Role, Braille “alert” <output>, Audio <output>, Braille aria-describedby, Audio aria-describedby, Braille Hidden Regions Hidden
NVDA / Firefox polite assertive polite assertive assertive buggy yes buggy polite assertive no no yes
JAWS / Chrome polite assertive polite assertive polite assertive no no polite assertive polite? assertive? yes
Narrator / Edge polite assertive polite assertive polite assertive no no polite assertive no no yes
VoiceOver macOS / Safari buggy buggy assertive assertive assertive assertive no no buggy buggy no no yes
Orca / Firefox polite polite buggy buggy polite no no yes
TalkBack / Chrome polite polite polite no polite no no yes
VoiceOver iDevice / Safari polite assertive assertive no polite no no yes

Detailed Results

For the read-all testing on macOS, I would activate it (Ctrl + Opt + A), then when a button had focus I would hit Enter. I could visually confirm the live region was populated and I had the Braille emulator running. NVDA’s read-all (Caps Lock + A) and JAWS’ read-all (Caps Lock + ) does not leave programmatic focus on controls while reading so the tactic does not work there.

I’m not a daily screen reader user and I used the Braille viewers built into each screen reader, so they may not reflect actual hardware exposure to users.

  • JAWS 2023.2306.38 + Chrome 114
    • All live regions treated as polite (announces at first break, which is not at end of sentence).
    • alert role does not pre-pend announcement with alert.
    • Each appeared as assertive in the Braille emulator.
    • No live region announces when hidden
  • JAWS 2026.2512.50 + Chrome 143.0.7499.193
    • All live regions treated as polite (announces at first break, which is not at end of sentence).
    • alert role does not pre-pend announcement with alert.
    • Each appeared as assertive in the Braille emulator.
    • The dynamic description announces and is exposed in Braille emulator.
    • No live region announces when hidden
  • JAWS 2026.2512.50 + Chrome 144.0.7559.60
    • All live regions treated as polite (announces at first break, which is not at end of sentence).
    • alert role does not pre-pend announcement with alert.
    • Each appeared as assertive in the Braille emulator.
    • The dynamic description does not announce nor is it exposed in the Braille emulator.
    • No live region announces when hidden
  • NVDA 2023.1 + Firefox 114
    • alert role treated as assertive (interrupts announcement but goes back and re-announces content it interrupted).
    • alert role pre-pends announcement with alert.
    • Only the alert role message appeared in the Braille emulator.
    • No live region announces when hidden
  • NVDA 2024.1 + Firefox 126
    • Polite, assertive, and <output> live regions treated as polite (announces at first break, which is not at end of sentence).
    • alert role treated as assertive (interrupts announcement but goes back and re-announces content it interrupted).
    • alert role pre-pends announcement with alert.
    • Each appeared as assertive in the Braille emulator.
    • The alert role was expsed in the Braille emulator only as “alert”, without the message..
    • No live region announces when hidden
  • NVDA 2025.3.2 + Firefox 146.0.1
    • Polite, assertive, and <output> live regions treated as polite (announces at first break, which is not at end of sentence).
    • alert role treated as assertive (interrupts announcement but goes back and re-announces content it interrupted).
    • alert role pre-pends announcement with alert.
    • alert role showed alert in Braille emulator, but not the rest of the message.
    • Each appeared as assertive in the Braille emulator.
    • The alert role was expsed in the Braille emulator only as “alert”, without the message..
    • The dynamic description does not announce nor is it exposed in the Braille emulator.
    • No live region announces when hidden
  • Narrator Win11 22621.1702 + Edge 114
    • All live regions treated as polite, waiting for first sentence to finish before announcing live region and then continuing to read content.
    • <output> is not announced at all. Likely a Narrator bug since it is not announced in Chrome or Firefox either. Though Chrome and Firefox both show it as a status region in Microsoft Accessibility Insights, Edge does not seem to expose it. Chatter on the socials suggests it might be a UIA bug.
    • alert role does not pre-pend announcement with alert.
    • It has no Braille emulator.
    • No live region announces when hidden
  • Narrator Win11 25H2 + Edge 143.0.3650.139
    • All live regions treated as polite, waiting for first sentence to finish before announcing live region and then continuing to read content.
    • Each appeared as assertive in the Braille emulator.
    • alert role does not pre-pend announcement with alert.
    • Each appeared as assertive in the Braille emulator.
    • The dynamic description does not announce nor is it exposed in the Braille emulator.
    • No live region announces when hidden
  • VoiceOver + macOS 12.6.6 + Safari 16.5
    • aria-live="polite" is not announced during read-all (Ctrl + Opt + A), but acts assertive in Braille displays and some content is lost.
    • aria-live="assertive" treated as assertive (interrupts announcement, stops reading).
    • alert role treated as assertive (interrupts announcement, stops reading).
    • alert role does not pre-pend announcement with alert.
    • <output> is not announced during read-all (Ctrl + Opt + A), but acts assertive in Braille displays and some content is lost.
    • No live region announces when hidden
  • VoiceOver + macOS 26.0.1 + Safari 26.0.1
    • aria-live="polite" is not announced during read-all (Ctrl + Opt + A), but acts assertive in Braille displays and some content is lost.
    • aria-live="assertive" treated as assertive (interrupts announcement, stops reading).
    • alert role treated as assertive (interrupts announcement, stops reading).
    • alert role does not pre-pend announcement with alert.
    • <output> announces twice and appears in Braille emulator twice.
    • The dynamic description does not announce nor is it exposed in the Braille emulator.
    • No live region announces when hidden
  • Orca + Ubuntu 24.10 + Firefox 131.0.2
    • All live regions treated as polite, waiting for end of first visual line (at wrap) before announcing live region and then continuing to read content.
    • alert role not announced at all.
    • The dynamic description does not announce.
    • I have no Braille emulator.
    • No live region announces when hidden
  • Orca + Ubuntu 25.04 + Firefox 146.0.1
    • All live regions treated as polite, waiting for end of first visual line (at wrap) before announcing live region and then continuing to read content.
    • alert role not announced at all.
    • The dynamic description does not announce.
    • I have no Braille emulator.
    • No live region announces when hidden
  • TalkBack 13.1 + Android 13 + Chrome 114
    • All live regions treated as assertive (interrupts announcement, does not go back and re-announce content it interrupted).
    • alert role does not pre-pend announcement with alert.
    • I have no Braille emulator.
    • No live region announces when hidden
  • TalkBack 16.0.0.777931756 + Android 16 + Chrome 143.0.7499.146
    • All live regions treated as polite.
    • alert role does not pre-pend announcement with alert.
    • I have no Braille emulator.
    • The dynamic description does not announce.
    • No live region announces when hidden
  • VoiceOver + iPadOS 16.5 + Safari 16.5
    • aria-live="assertive" and alert role treated as assertive (interrupts announcement but goes back and re-announces content it interrupted).
    • alert role does not pre-pend announcement with alert.
    • I have no Braille emulator.
    • No live region announces when hidden
  • VoiceOver + iPadOS 26.2 + Safari 26.2
    • aria-live="assertive" and alert role treated as assertive (interrupts announcement but does not go back and re-announces content it interrupted).
    • alert role does not pre-pend announcement with alert.
    • I have no Braille emulator.
    • The dynamic description does not announce.
    • No live region announces when hidden

Wrap-up

This reflects support as noted for the latest releases in the Detailed Results section. If you find something amiss, leave a comment. It’s possible I got something wrong during transcription or testing. Or just, you know, life.

]]>
https://adrianroselli.com/2026/01/live-region-support.html/feed 2
Brief Note on Application Keyboard Shortcuts https://adrianroselli.com/2026/01/brief-note-on-application-keyboard-shortcuts.html https://adrianroselli.com/2026/01/brief-note-on-application-keyboard-shortcuts.html#comments Fri, 09 Jan 2026 18:45:04 +0000 https://adrianroselli.com/?p=45159 Identifying keyboard shortcuts for an application is mostly an internationalization problem. It’s also not a new problem. A recent (to me) example is the WordPress Gutenberg team starting to discuss keyboard shortcuts in 2017, addressing what will and won’t work across keyboards for different languages.

A Mac keyboard with a hand contorting its fingers to as it presses Control, Option, Caps Lock, and a fourth finger is reaching for the J.
Sight gag for my old joke that navigating text fields in NVDA and JAWS is just pressing E, but in VoiceOver it’s pressing Caps Lock + Ctrl + Option + + J.

It’s a fascinating thread given all the international participation. It’s also outside the scope of this post.

Instead, this post is to tell you not to stress about what keys a screen reader uses. This is because screen readers have pass-through commands, which tell the screen reader to ignore the next key or combo and pass it through to the application (whether an installed application or the web browser running your application).

Windows desktop screen readers have explicit pass-through commands:

NVDA
NVDA + F2
JAWS
JAWS + 3
Narrator (Microsoft has a poor method of selecting between Win10 and Win11 on that page, so the text link may not work)
Narr + 3

VoiceOver on macOS does as well, and I’m being very careful to qualify this as macOS, not iOS nor iPadOS:

VoiceOver on macOS
VO + Tab

VoiceOver on macOS differs from Windows screen readers by requiring the modifier keys to execute most commands. But if you enable Quick Nav to allow single key use, you may need to turn it off sometimes (which is not a pass-through):

Toggle single-key Quick Nav
VO + Q
Toggle arrow-key Quick Nav
VO + Shift + Q

Orca generally relies on a modifier key except in structural navigation, which you may need to disable (and is not the same as a pass-through):

Toggle structural navigation
Orca + Z

In each case, the screen reader modifier key (NVDA or VO, for example) represents the Caps Lock or Insert (depending on preferences and keyboard layout), and for VoiceOver are the Ctrl + Opt keys combined.

This, of course, assumes the screen reader audience for your application knows they exist. Not all screen reader users are expert screen reader users (imagine the same breakdown as with the general public and their computers). But this is stuff you would include in documentation and training (because you have documentation, right?) while also telling your developers and QA folks so they don’t file unnecessary bugs.

Update: 22 January 2026

James Scholes told me that VoiceOver on macOS does indeed have a pass-through command. I updated the post accordingly. I didn’t do my usual strikes to show the changes, mostly because it was getting unwieldy to read (I moved the Orca section to after macOS).

]]>
https://adrianroselli.com/2026/01/brief-note-on-application-keyboard-shortcuts.html/feed 1
How I Evaluate an ACR (VPAT®) https://adrianroselli.com/2026/01/how-i-evaluate-an-acr-vpat.html https://adrianroselli.com/2026/01/how-i-evaluate-an-acr-vpat.html#respond Tue, 06 Jan 2026 00:02:54 +0000 https://adrianroselli.com/?p=38881 ACRs are Accessibility Conformance Reports, which are the output of a VPAT, or Voluntary Product Accessibility Template maintained by ITIC, or the Information Technology Industry Council (which is why VPAT often has a ® symbol hanging off it). An organization may fill out the template to indicate how or if its offering conforms to WCAG, Section 508, EN 301 549, or all three.

KitchenAid with meat grinder attachment, labeled ‘VPAT®’, an arm labeled ‘vendor’ pushing food labeled ‘claims’ into the grinder, and a bowl beneath catching the results labeled ‘ACR’.
The process simplified: vendors feed their accessibility claims into the template to produce a report.

I’ve helped evaluate ACRs for clients looking to purchase a product but who want guarantees that the product is accessible. The existence of an ACR doesn’t mean the product is accessible or even conformant, nor does it mean the ACR itself is accurate. However, it’s often the first step to identifying how a company presents the accessibility of its offerings. I often remind my clients looking to acquire a product that “ACR or GTFO” is only a starting point.

This post outlines, at a very high level, some of the steps I take when evaluating an ACR and, by extension, a vendor. You may have other approaches. You should comment with your own. Unless you feel it’s some sort of trade secret. Which is weird.

Steps

This list is not exhaustive and not always practical. It matches the front matter provided in the template.

  1. Name of Product/Version: You want this to match the product and release version you’re considering using.
  2. Report Date:
    • If undated, it should match the current VPAT release.
    • The freshness could be a function of the product’s deployment process.
    • Products following a continuous deployment (or unversioned) release cycle should be as current as possible.
    • Otherwise the update cadence should probably be under one year.
  3. Product Description: Make sure the product description in the ACR matches the product you’re considering.
  4. Contact Information: This should be the product owner, ACR author, or otherwise make sense in context. If it’s a completely different company, one that may not be on your contract, ask for clarity.
  5. Notes: This is where you pay attention to…
    • Carve-outs and exceptions which can exclude entire sections or features of a product from the report.
    • Language that gets squishy and tries to frame problems identified in the report.
    • This may be where the team performing the testing is identified.
  6. Evaluation Methods Used:
    • You don’t want a report that relies solely on automated testing or “product knowledge.”
    • Look for version number of browsers, operating systems, and assistive technologies and ensure they are recent and / or match your audience.
    • Be cautious of testing performed in only one browser or only one screen reader or that doesn’t mention voice control or other assistive technologies. For example, if it was only tested in Safari with VoiceOver on a Mac, then it’s not a thorough review.
  7. Applicable Standards / Guidelines: It’s critical this matches the latest of each as well as the latest VPAT version. A WCAG review that only looks at WCAG 2.0 neglects the reality that you and your audience aren’t in 2008.
    • The VPAT is currently 2.5, or April 2025, and comes in the following flavors:
      • VPAT 2.5Rev EU (April 2025) covers EN 301 549.
      • VPAT 2.5Rev WCAG (April 2025) covers WCAG 2.2.
      • VPAT 2.5Rev 508 (April 2025) covers Revised Section 508 standards.
      • VPAT 2.5Rev INT (April 2025) covers all three.

This list addresses the content of the report itself.

  1. Go back to the front matter and identify the report author (check Contact Information, Notes, or Evaluation Methods Used).
    1. The author is also the vendor — be very wary.
    2. The author is a digital accessibility consultancy you recognize — be less wary.
    3. The author is a digital accessibility consultancy you do not recognize — do some research:
      • Look for an accessibility policy on the author’s site.
      • Find accessibility issues / contributions on GitHub.
      • Check if any issues / PRs are tagged as accessibility.
      • Look at how long it takes to close accessibility issues.
      • See if the author speaks to accessibility in its blog, social media, and so on.
      • Try to find any disability or accessibility community engagement.
      • Review the contracts for any language related to WCAG (or applicable laws).
      • Try to find any written accessibility guarantees.
      • Ask for a demo of the accessibility features of the product.
      • If the reviewers are Akbar and Jeff’s WCAG Hut then maybe put it under a bit more scrutiny.
  2. Every row should have content in the Remarks and Explanations cell.
    • These should detail what is wrong and ideally what is testably wrong (so you can confirm it and understand the impact).
    • They should describe support as well, if only to brag about the effort, but typically don’t.
    • References to internal bugs are unlikely, but you can ask for the status and timelines of bugs related to your concerns.
    • Make sure the remarks demonstrate understanding of Success Criterion, etc., especially for anything marked N/A (pasting SC language is not understanding).
    • Within an INT ACR, look for cross-mappings across WCAG, Section 508, and EN 301 549.

Way back in 2011, Karl Groves wrote Why a Third Party Should Prepare Your VPAT and I think it’s useful for evaluating ACRs. In 2023 he also wrote A quick guide to reviewing a VPAT ACR, which is far longer than this post. Probably read it after this one for a more detail. There is also WebAIM’s process for vetting ACRs as another reference point, from early 2024.

]]>
https://adrianroselli.com/2026/01/how-i-evaluate-an-acr-vpat.html/feed 0
My Use of ‘AI’ on this Site https://adrianroselli.com/2025/12/my-use-of-ai-on-this-site.html https://adrianroselli.com/2025/12/my-use-of-ai-on-this-site.html#comments Mon, 29 Dec 2025 21:35:16 +0000 https://adrianroselli.com/?p=43774 I’m using this post to acknowledge my past practices and establish future ones on this site related to ‘AI’.

LLM in a red circle and slash.
  • I have not, and will not, use LLMs to write, draft, review, or otherwise participate in content creation — outside of clearly-identified contexts to critique it.
  • I have used generative technologies to create images for posts, but have abandoned the practice. Those instances are listed below.
  • I have no interest in becoming a blog about what is broadly (and inaccurately) called ‘AI’ nor one that spends much time dwelling on it. I have written only a handful of posts on it. Those instances are listed below.
  • I do not publish comments that are spam, and since the overwhelming majority of spam comments over the last year also appear to be LLM-generated, that means I am probably also blocking comments written by an LLM.

Generated Images

Cases through the end of 2025 where I have used generated images which were not part of the core subject of the post (as a critique or evaluation):

  1. CSUNATC: WCAGmire (the entire talk was made of generated images, so I am not changing the post), 22 March 2024
  2. Under-supported and Underpaid, 19 January 2024 (I am not changing the image since it’s a parody of a generated image)
  3. Web Development Advent Calendars for 2023, 1 December 2023
  4. Øredev 2023: WCAGmire (the entire talk was made of generated images, so I am not changing the post), 8 November 2023
  5. Don’t Turn a Table into an ARIA Grid Just for a Clickable Row, 2 November 2023
  6. Paris Web 2023: WCAGmire (the entire talk was made of generated images, so I am not changing the post), 28 September 2023
  7. Browser Video Players Review, 5 September 2023
  8. Don’t Override Screen Reader Pronunciation, 11 April 2023
  9. ARIA vs HTML, 21 February 2023
  10. Avoid Spanning Table Headers, 20 February 2023
  11. Comparing Manual and Free Automated WCAG Reviews, 19 January 2023
  12. The 411 on 4.1.1, 5 December 2022, replaced 5 January 2026
  13. Your Accessibility Claims Are Wrong, Unless…, 14 November 2022, replaced 5 January 2026
  14. Accessibility ‘Gaps’ in MVPs, 5 November 2022, replaced 5 January 2026
  15. Under-Engineered Patterns for #a11yTOconf (the entire talk was made of generated images, so I am not changing the post), 20 October 2022
  16. role=dice for #a11yTOgaming (much of the talk was made of generated images, so I am not changing the post), 19 October 2022
  17. ‘Accessibility at the Edge’ W3C CG Is an Overlay Smoke Screen, 1 September 2022, replaced 5 January 2026
  18. Conveying All-Caps Legal Text, 27 August 2022, replaced 5 January 2026
  19. Internet Explorer Still Does Not Go Away Today (I plan to leave this image given the context it offers), 15 June 2022
  20. Keyboard-Only Scrolling Areas (I plan to leave this image given what it demonstrates), 1 June 2022
  21. Keyboard Challenges for Twitter’s New ALT Badge (I plan to leave the images owing to context), 9 April 2022

You can see where my fascination and experimentation dropped off. My use of generated images was never acceptable. Not because I was depriving designers of work, but because the work I generated was based on theft. I did it anyway. Some of the images were meant to demonstrate failures of the technology, but not all. In time I hope to replace nearly all generated images (keeping the ones where I am evaluating the output).

Post about ‘AI’

Cases through the end of 2025 where I have written about ‘AI’ as the core subject of a post:

  1. This post
  2. OpenAI, ARIA, and SEO: Making the Web Worse, 22 October 2025
  3. Generic LLM Chatbot Attestation, 16 February 2025
  4. What You Can Do as a Web Builder on Earth Day, 22 April 2024
  5. No, ‘AI’ Will Not Fix Accessibility, 8 June 2023
  6. AI-Generated Images from AI-Generated Prompts, 9 January 2023
  7. AI-Generated Images from AI-Generated Alt Text, 4 August 2022

I think it’s fair to say this blog has not turned into a pro- or anti-‘AI’ collection of posts. Generally I only touch on it when it directly impacts my work.

Wrapping

All this together is me coming clean where I have cheated (images) and also committing to you (and me) that this has always been and will continue to be a site written by a human (still me). On top of that, this site is not following the fad of trying to chase the trend and write about every iteration of ‘AI’.

Similarly, this site has no ads, does not gather nor sell your information, and has no third-party trackers. That said, I am exploring adding some kind of analytics to the site — but not Google.

Like most writers, my content has also been stolen. I am not actively poisoning LLMs, but I rather want to.

Me, as a Reader

When reading content on other sites, if I sense an article or post has been generated, drafted, edited, or otherwise touched by an LLM, I close the site and add it to a block list. I should note that I have been much less strict about generated images when reading content on other sites. Yes, I still have to reconcile that. I am still torn about cases of LLM-generated image alternatives, but if it’s from an accessibility practitioner and has clearly not been reviewed, then possible block. I accept auto-generated closed captions because I recognize those are the only captions I will generally get.

I look forward to a 2026 crafted by humans, even if it means I have to block everything else.

]]>
https://adrianroselli.com/2025/12/my-use-of-ai-on-this-site.html/feed 7
You Can’t Make Something Accessible to Everyone https://adrianroselli.com/2025/12/you-cant-make-something-accessible-to-everyone.html https://adrianroselli.com/2025/12/you-cant-make-something-accessible-to-everyone.html#comments Tue, 02 Dec 2025 20:37:57 +0000 https://adrianroselli.com/?p=43541 This post’s title is unpleasant, but it’s important to acknowledge the reality of the human condition and limitations in technologies. Even purpose-built assistive tech.

Broadly, when someone says something is “accessible” that’s a hopeful statement that is based on some best efforts. Of course, there are bad actors who assert something is accessible because they just want the clicks or don’t invest much effort. Others assert their pet features are to “make something accessible” but are unable to explain how. Some even promise the technology to do iteven formerly legitimate practitioners.

There are many people who think making a web site that passes WCAG 2.2 at Level AA makes it accessible. I’ve documented many cases where that’s not true, as have others, some for years now.

If you think passing WCAG at Level AAA will be more accessible, then I know plenty of people who’d like to have a conversation with SC 1.4.6: Contrast (Enhanced) for encoding eye pain.

I keep trying to stress (with authors, clients, spec writers, GitHub randos, LLM aficionados, …) that accessibility is about people. It is not a strictly technical problem to be solved with code.

Because people have varying needs across disparate contexts from assorted expectations with unequal skill levels using almost random technologies, never mind current moods and real-life distractions, to suggest one thing will be accessible for everyone in all those circumstances is pure hubris. Or lack of empathy. Maybe a mix.

I’m not suggesting that claiming something is “accessible” is an overtly bad act. I am saying, however, that maybe you should explain what accessibility features it has, and let that guide people. It’s more honest to them and you.

This is why we keep working at accessibility. To shrink those cases. To move halfway to the wall, over and over, until we cannot slide a sheet of paper between our nose and the cold vinyl siding of reality. That’s why we keep up the work. That’s why we continue to try. To make things better for yet more people, even if we can’t cover everyone.

Stop beating yourself up and be wary of those who maybe aren’t.

]]>
https://adrianroselli.com/2025/12/you-cant-make-something-accessible-to-everyone.html/feed 3
Web Design / Dev Advent Calendars for 2025 https://adrianroselli.com/2025/12/web-design-dev-advent-calendars-for-2025.html https://adrianroselli.com/2025/12/web-design-dev-advent-calendars-for-2025.html#comments Mon, 01 Dec 2025 15:05:55 +0000 https://adrianroselli.com/?p=41344
A boxed Charlie Brown lonely tree (a spindly pine stick with a single ornament bending it to the ground) on a carpet of concentric black and white diamonds.
The advent calendar I wanted to use for the photo hasn’t arrived yet, so enjoy this box of tree.

Web developers around the world have for years given a nod to Saturnalia solstice Isaac Newton’s birthday Yule wassailing mummering end of Gregorian calendar year Christmas with advent calendars covering web-related topics. As a result, you may (should) recognize some of the ones listed below.

Every year I miss a few on December 1, so add a comment or hit me up on the Fediverse if you have more. I delayed posting again this year because I am a bit tired of people who just copy the entire thing and share their version on the socials. Also, this content originally appeared on AdrianRoselli.com and if you aren’t reading it there then the site you are on is a thieving site which should be held in the most contemptible disdain. Happy holidays!

I have not included advent calendars that are single blog posts, image-only efforts with inaccessible images, or delivered only via email. It would be a terrible gift from me to you if you sign up for spam or end up taking advice from organizations that are clearly bad at advice. Maybe next year I’ll ban LinkedIn posts. I also don’t evaluate the quality of the calendars (hard to do when most have barely opened their first day).

I am happy to see more of the calendar creators are available on the fediverse. I look forward to it being all of them. I resent those who are still on Twitter, so if you have a better social link for any of them, please let me know.

HTMHell Advent Calendar (@matuzo@front-end.social)

HTMHell Advent Calendar is Manuel Matuzović’s doubling-down on the theology of HTML. And Christianity. One of those. Anyway, each day will reveal an article, talk, or tool that focuses on HTML.

Advent of Code (@ericwastl@hachyderm.io)

Advent of Code provides a small programming puzzle each of twelve days (that’s a change this year). They are stand-alone, but supposedly have a general theme. They also use different technologies so there is some variety as well. This year, in honor of 10 years, Eric Wastl made a music video for it.

24 Jours de Web (@ParisWeb@mamot.fr)

24 Jours de Web is back as an advent calendar for web folk. Written in French, it is clearly primarily targeted at French speakers, but a round of Google Translate will open it up to far more readers (like me).

Performance Calendar (@[email protected])

Performance Calendar hails this as the speed geek’s favorite time of the year, ostensibly because of the tips it has been offering each December since 2009. It isn’t just server optimizations you’ll find here, so don’t shy away because you’re not a system admin.

JVM Advent (@JavaAdvent)

JVM Advent is posting a technical article from various authors related to Java each day.

アクセシビリティ Advent Calendar 2025 (@motchie)

Accessibility (アクセシビリティ) Advent Calendar 2024 is in Japanese, and thanks to the powers of Google Translate, I can tell you it covers a variety of accessibility issues, including web: Webのアクセシビリティを含む、様々なアクセシビリティについてのアドベントカレンダーです。). If you know Japanese, I welcome any corrections. It has been running since 2013.

Selfhtml Advent (@SELFHTML@bildung.social)

Selfhtml Advent is from Germany’s oldest (since 1995) and largest web design Community. The advent calendar will present tips and examples from its contributors. As of 1 December, there doesn’t appear to be content, however.

Kodekalender (@knowitnorge)

Kodekalender is a Norwegian code-specific calendar. Each day solve a code puzzle and be entered in a drawing (you should check the rules).

Bekk Christmas (LinkedIn: bekk)

Bekk.christmas collects a few topics per day in one calendar. In previous years I broke them all out, but with some of the domains not resolving it seems safest to link to the parent calendar. Posts will cover JavaScript, UX, security, and machine learning.

24 Days in Umbraco (@24DaysInUmbraco@umbracocommunity.social)

24 Days in Umbraco is dedicated to the Umbraco CMS. This calendar has been running since 2012.

24 Pull Requests (@24PullRequests@mastodon.social)

24 Pull Requests is less an advent calendar than it is an effort to mobilize developers. The goal is to get developers to send a pull request every day in December (up to Christmas), thereby supporting your favorite open source projects. I’m not sure it’s active in the sense that it posts, but you can still do your own contributions of course.

Perl Weekly Challenge Advent (@PerlWChallenge)

Perl Weekly Challenge Advent has been running since 2021. Each day it takes a response from a previous weekly challenge and re-posts it on the site.

Perl Advent Calendar (@perladvent)

Perl Advent Calendar goes all the way back to 2000 (and back then looked a bit more like a traditional advent calendar, too) and has been dispensing tips for Perl developers ever since.

C# Advent Calendar (@mgroves)

C# Advent Calendar (sharp, not hashtag) is revealing two posts per day, including on December 25. That’s 50 posts over the course of the month.

Raku Advent Calendar

Raku Advent Calendar (Raku is Perl6) has a new daily post for your Perl/Raku needs.

Festive Tech Calendar (@_cloudfamily)

Festive Tech Calendar is a YouTube channel with videos ranging from a half hour to over an hour.

IndieWeb Gift Calendar

IndieWeb Gift Calendar is an annual group effort starting in 2017 to gift (ship) one or more IndieWeb-related thing(s) each day of December that others can use to improve their IndieWeb experience.

The Tactile Times Advent Calendar and Countdown

The Tactile Times Advent Calendar and Countdown is self-described as the first fully-accessible online advent calendar aimed specifically at young braillists (as far as they know). Its content is not web/dev-related, but it is good insight into what “3 blind children” can build for their “newspaper for young braillists”.

CSS Advent Calendar (@stevenaharrison)

CSS Advent Calendar offers a brief note on a property or feature, with links to MDN and a related video. He’s also posting a very brief video on Twitter for each day.

F# Advent Calendar in English

F# Advent Calendar in English collects folks who write posts or make videos about solving something in F#. The English version dates back to 2014 and there is a Japanese version back to 2010. It also collects folks who solve Advent of Code using F#.

adventJS

adventJS presents programming challenges to be solved using JavaScript, TypeScript and / or Python (though that one appears to still be in beta). It has been running since 2020 and appears to be available in Spanish and Portuguese.

Advent 2025 – 24 days of accessibility (@freelock@drupal.community)

Advent 2025 – 24 days of accessibility plans to highlight a section (or more) of the Web Content Accessibility Guidelines (WCAG) each day.

Advent of A11Y

Advent of A11Y will offer a tip every day until Christmas how to make your web content more accessible. It promises each tip will be quick to read and easy to apply. Thanks to Jens in the comments for this one.

Advent of Svelte

Advent of Svelte has no coding challenges, focusing instead on 24 days of Svelte knowledge, tips, and insights. Started in 2023, it used to focus on Svelte coding challenges. But in this economy‽

Debug December

Debug December presents 24 coding challenges framed as bugs for you to solve. There’s also a raffle on days 3, 6, 12, 18 and 24. Thanks to T on Bluesky for the heads-up.

Advent(ure) in System Seeing

Advent(ure) in System Seeing provides a daily activity to help you practice how you might perceive or understand a system so you can better design it. Collected, these become a journal of prompts for your system design and architecture work.

Extras

Inclusive Design 24 (#id24) is an online conference that stuffs 24 talks into 24 consecutive hours. Thanks to a fluke of timekeeping, you can make your own advent calendar from #id24 videos for any year. For example, head over to the 2025 #id24 schedule and watch the one that corresponds to the day of the month. Free content! No sign-in needed!

Each day until Christmas Heydon is posting on Mastodon a new shirt design in a series he calls “monochrome pride:” celebrating pride but without all the bright colors.

Damien Guard is returning with his Masto-only 8×8 Font Advent Calendar. The hashtag is #AdventOfFonts, so you can follow on your preferred Masto client / server.

Previous Years

I started tracking these in 2010. Since then some have come and gone. For the ones not returning, in many cases the content is still out there. Take a look and maybe you’ll find an older article that is useful today.

]]>
https://adrianroselli.com/2025/12/web-design-dev-advent-calendars-for-2025.html/feed 9