Snippets

The title attribute is an accessibility fallback

[S]upport aside, it’s still not a particularly good choice as a consistent means to convey accessible information. Because while the title does provide an accessible name to elements in the absence of other sources, it is considered a fallback. Outside of some notable exceptions (more on this later), other mechanisms will always be preferred.

[…]

While the title attribute can be used on any HTML element, it’s essentially wasted on most inline, text level elements. As these elements aren’t typically included in the accessibility tree, there’s no reason for a screen reader to look for a title on these elements to announce.

Block-level wrapping elements can get some usage out of the title attribute. JAWS, NVDA and VoiceOver will all announce a title on elements like landmarks (header, footer, main, etc.) but support may vary on other elements depending on your browser pairing. For example, JAWS will not announce a title on a div without additional role updates.

Other wrapping elements, like lists and paragraphs, are announced in JAWS and VoiceOver, but NVDA ignores the attribute on these elements. But honestly, using title tooltips on these elements is highly suspect. Why would you want to have a tooltip constantly appearing over a large chunk of content? Unless you are purposefully trying to hide content, which isn’t helpful, there’s little need for using the title in this manner.

Images, form fields, and anchor links are the elements one is most likely to associate with the title attribute. In regards to screen readers, the title attribute essentially gets a “B” grade when reviewing publicly available screen reader support charts from powermapper.com.

titles are meant to serve as descriptive text. And largely only in situations where there is no accessible name for an image, form field, or anchor element, the title will be promoted to the accessible name.

CSS Custom Property fallbacks

Here’s a thing I didn’t know you could do with var(): like font stacks, you can specify a fallback value if the specified custom property isn’t defined, like so:

Code language: CSS

body {
  color: var(--color-text-default, black); 
}

Mind you, this only applies to browsers that understand custom properties. To provide a fallback for browsers that don’t, use the tried and tested method of defining the fallback first and then the newer property declaration:

Code language: CSS

body {
  color: black;
  color: var(--color-text-default, black); 
}

The Story of CSS Grid, from Its Creators

On October 17th, Microsoft’s Edge browser shipped its implementation of CSS Grid. This is a milestone for a number of reasons. First, it means that all major browsers now support this incredible layout tool. Second, it means that all major browsers rolled out their implementations in a single year(!), a terrific example of standards success and cross-browser collaboration. But third, and perhaps most interestingly, it closes the loop on a process that has been more than 20 years in the making.

[…]

“I don’t recall a feature ever shipping like CSS Grid has shipped. Every major browser will have shipped it within a matter of a single year, and it will be interoperable because we’ve been… implementing [it] behind flags, testing it, making future changes behind flags, and then when it was deemed stable, all the browsers are now shipping it natively.”

“With everybody shipping at approximately the same time,” Atkins said, “[Grid] goes from an interesting idea you can play with to something that you just use as your only layout method without having to worry about fallbacks incredibly quickly. … [It’s been] faster than I expected any of this to work out.”

[…]

“It is the most powerful layout tool that we have invented yet for CSS,” [Tab] Atkins said. “It makes page layouts so ridiculously easy. … [P]eople have always been asking for better layouts. Just for author-ability reasons and because the hacks that we were employing weren’t as powerful as the old methods of just put[ting] it all in a big old table element—that was popular for a reason; it let you do powerful complex layouts. It was just the worst thing to maintain and the worst thing for semantics. And Grid gives you back that power and a lot more, which is kind of amazing.”

An Inclusive Content Slider (Carousel)

Carousels (or ‘content sliders’) are like men. They are not literally all bad — some are even helpful and considerate. But I don’t trust anyone unwilling to acknowledge a glaring pattern of awfulness. Also like men, I appreciate that many of you would rather just avoid dealing with carousels, but often don’t have the choice. Hence this article.

Carousels don’t have to be bad, but we have a culture of making them bad. It is usually the features of carousels, rather than the underlying concept that is at fault. As with many things inclusive, the right solution is often not what you do but what you don’t do in the composition of the component.

[…]

  • Use list markup to group the slides together. Then screen reader users in ‘browse’ mode can use list navigation shortcuts to traverse them.
  • Provide a reasonable experience in HTML with CSS, then feature detect when enhancing with JavaScript.
  • Don’t preload content users are not likely to see. Defer until they perform an action to see it.
  • Provide generous touch targets for touch users on mobile / small screens.
  • If in doubt of a control’s (or widget’s) affordance, spell it out with instructions
  • If you are a man and got past the first paragraph without being personally offended: Congratulations! You do not see men and women as competing teams.

Prevent image loading with MutationObserver

Edit: this is not actually reliable due to the way that various browsers will try to pre-emptively fetch resources as they parse an HTML document, as confirmed by Jake Archibald of the Chrome dev team.

It turns out that if you place a MutationObserver in the <head> (or presumably even in the <body> before any <img> elements), and have it start observing right away it will be able to remove the src before the browser downloads the image, thus allowing for lazy loading and other optimizations.

I put this in <head> and normal <img> tags in <body>. - No image requests visible in Firefox dev tools - Safari shows requests but 0 bytes transferred - Chrome seems to get 4 kb over the wire before calling it quits

Code language: JavaScript

const observer = new MutationObserver(mutations => {
  mutations.forEach(mutation => {
    Array.from(mutation.addedNodes)
      .filter(node => node.tagName === 'IMG')
      .forEach(img => {
        img.dataset.src = img.src;
        img.src = '';
      });
  });
});
 
observer.observe(document.documentElement, {
  childList: true,
  subtree: true
});

Introduction to HTML5 form validation

I recently embarked on improving the client-side form validation for a client. There were about 400 lines of form validation code stuffed inside a 1000 line form_helper.js. I looked for lightweight form validation scripts but after some hemming and hawing I decided to try my hand (again) at native HTML5 Form Validation.

If you’ve ever experimented with HTML5 Form Validation, you’ve probably been disappointed. The out-of-box experience isn’t quite what you want. Adding the required attribute to inputs works wonderfully. However the styling portion with input:invalid sorta sucks because empty inputs are trigger the :invalid state, even before the user has interacted with the page.

I finally sat down and spent a couple days trying to make HTML5 Form Validation work the way I want it. I had the following goals:

  1. Leverage browser-level feedback, free focus management and accessible labelling
  2. Only validate inputs on submit
  3. Styling with .error class

With this wishlist in hand, I set off and found a solution that works with only 6 lines of code.

Network based image loading using the Network Information API in Service Worker

Recently, Chromium improved their implementation of navigator.connection by adding three new attributes: effectiveType, downlink and rtt.

Before that, the available attributes were downLinkMax and type. With these two attributes you couldn’t really tell if the connection was fast or slow. The navigator.connection.type may tell us a user is using WiFi, but this doesn’t say anything about the real connection speed, as they may be using a hot spot and the connection is in fact 2G.

With the addition of effectiveType we are finally able to get the real connection type. There are four different types (slow-2g, 2g, 3g and 4g) and they are described this way by the Web Incubator Community Group:

slow-2g: The network is suited for small transfers only such as text-only pages.
2g: The network is suited for transfers of small images.
3g: The network is suited for transfers of large assets such as high resolution images, audio, and SD video.
4g: The network is suited for HD video, real-time video, etc.

Let’s see how we can improve user experience by delivering images based on available connection speed.