Tag: Accessibility

Making Calendars With Accessibility and Internationalization in Mind

Doing a quick search here on CSS-Tricks shows just how many different ways there are to approach calendars. Some show how CSS Grid can create the layout efficiently. Some attempt to bring actual data into the mix. Some rely on a framework to help with state management.

There are many considerations when building a calendar component — far more than what is covered in the articles I linked up. If you think about it, calendars are fraught with nuance, from handling timezones and date formats to localization and even making sure dates flow from one month to the next… and that’s before we even get into accessibility and additional layout considerations depending on where the calendar is displayed and whatnot.

Many developers fear the Date() object and stick with older libraries like moment.js. But while there are many “gotchas” when it comes to dates and formatting, JavaScript has a lot of cool APIs and stuff to help out!

January 2023 calendar grid.

I don’t want to re-create the wheel here, but I will show you how we can get a dang good calendar with vanilla JavaScript. We’ll look into accessibility, using semantic markup and screenreader-friendly <time> -tags — as well as internationalization and formatting, using the Intl.Locale, Intl.DateTimeFormat and Intl.NumberFormat-APIs.

In other words, we’re making a calendar… only without the extra dependencies you might typically see used in a tutorial like this, and with some of the nuances you might not typically see. And, in the process, I hope you’ll gain a new appreciation for newer things that JavaScript can do while getting an idea of the sorts of things that cross my mind when I’m putting something like this together.

First off, naming

What should we call our calendar component? In my native language, it would be called “kalender element”, so let’s use that and shorten that to “Kal-El” — also known as Superman’s name on the planet Krypton.

Let’s create a function to get things going:

function kalEl(settings = {}) { ... }

This method will render a single month. Later we’ll call this method from [...Array(12).keys()] to render an entire year.

Initial data and internationalization

One of the common things a typical online calendar does is highlight the current date. So let’s create a reference for that:

const today = new Date();

Next, we’ll create a “configuration object” that we’ll merge with the optional settings object of the primary method:

const config = Object.assign(   {     locale: (document.documentElement.getAttribute('lang') || 'en-US'),      today: {        day: today.getDate(),       month: today.getMonth(),       year: today.getFullYear()      }    }, settings );

We check, if the root element (<html>) contains a lang-attribute with locale info; otherwise, we’ll fallback to using en-US. This is the first step toward internationalizing the calendar.

We also need to determine which month to initially display when the calendar is rendered. That’s why we extended the config object with the primary date. This way, if no date is provided in the settings object, we’ll use the today reference instead:

const date = config.date ? new Date(config.date) : today;

We need a little more info to properly format the calendar based on locale. For example, we might not know whether the first day of the week is Sunday or Monday, depending on the locale. If we have the info, great! But if not, we’ll update it using the Intl.Locale API. The API has a weekInfo object that returns a firstDay property that gives us exactly what we’re looking for without any hassle. We can also get which days of the week are assigned to the weekend:

if (!config.info) config.info = new Intl.Locale(config.locale).weekInfo || {    firstDay: 7,   weekend: [6, 7]  };

Again, we create fallbacks. The “first day” of the week for en-US is Sunday, so it defaults to a value of 7. This is a little confusing, as the getDay method in JavaScript returns the days as [0-6], where 0 is Sunday… don’t ask me why. The weekends are Saturday and Sunday, hence [6, 7].

Before we had the Intl.Locale API and its weekInfo method, it was pretty hard to create an international calendar without many **objects and arrays with information about each locale or region. Nowadays, it’s easy-peasy. If we pass in en-GB, the method returns:

// en-GB {   firstDay: 1,   weekend: [6, 7],   minimalDays: 4 }

In a country like Brunei (ms-BN), the weekend is Friday and Sunday:

// ms-BN {   firstDay: 7,   weekend: [5, 7],   minimalDays: 1 }

You might wonder what that minimalDays property is. That’s the fewest days required in the first week of a month to be counted as a full week. In some regions, it might be just one day. For others, it might be a full seven days.

Next, we’ll create a render method within our kalEl-method:

const render = (date, locale) => { ... }

We still need some more data to work with before we render anything:

const month = date.getMonth(); const year = date.getFullYear(); const numOfDays = new Date(year, month + 1, 0).getDate(); const renderToday = (year === config.today.year) && (month === config.today.month);

The last one is a Boolean that checks whether today exists in the month we’re about to render.

Semantic markup

We’re going to get deeper in rendering in just a moment. But first, I want to make sure that the details we set up have semantic HTML tags associated with them. Setting that up right out of the box gives us accessibility benefits from the start.

Calendar wrapper

First, we have the non-semantic wrapper: <kal-el>. That’s fine because there isn’t a semantic <calendar> tag or anything like that. If we weren’t making a custom element, <article> might be the most appropriate element since the calendar could stand on its own page.

Month names

The <time> element is going to be a big one for us because it helps translate dates into a format that screenreaders and search engines can parse more accurately and consistently. For example, here’s how we can convey “January 2023” in our markup:

<time datetime="2023-01">January <i>2023</i></time>

Day names

The row above the calendar’s dates containing the names of the days of the week can be tricky. It’s ideal if we can write out the full names for each day — e.g. Sunday, Monday, Tuesday, etc. — but that can take up a lot of space. So, let’s abbreviate the names for now inside of an <ol> where each day is a <li>:

<ol>   <li><abbr title="Sunday">Sun</abbr></li>   <li><abbr title="Monday">Mon</abbr></li>   <!-- etc. --> </ol>

We could get tricky with CSS to get the best of both worlds. For example, if we modified the markup a bit like this:

<ol>   <li>     <abbr title="S">Sunday</abbr>   </li> </ol>

…we get the full names by default. We can then “hide” the full name when space runs out and display the title attribute instead:

@media all and (max-width: 800px) {   li abbr::after {     content: attr(title);   } }

But, we’re not going that way because the Intl.DateTimeFormat API can help here as well. We’ll get to that in the next section when we cover rendering.

Day numbers

Each date in the calendar grid gets a number. Each number is a list item (<li>) in an ordered list (<ol>), and the inline <time> tag wraps the actual number.

<li>   <time datetime="2023-01-01">1</time> </li>

And while I’m not planning to do any styling just yet, I know I will want some way to style the date numbers. That’s possible as-is, but I also want to be able to style weekday numbers differently than weekend numbers if I need to. So, I’m going to include data-* attributes specifically for that: data-weekend and data-today.

Week numbers

There are 52 weeks in a year, sometimes 53. While it’s not super common, it can be nice to display the number for a given week in the calendar for additional context. I like having it now, even if I don’t wind up not using it. But we’ll totally use it in this tutorial.

We’ll use a data-weeknumber attribute as a styling hook and include it in the markup for each date that is the week’s first date.

<li data-day="7" data-weeknumber="1" data-weekend="">   <time datetime="2023-01-08">8</time> </li>

Rendering

Let’s get the calendar on a page! We already know that <kal-el> is the name of our custom element. First thing we need to configure it is to set the firstDay property on it, so the calendar knows whether Sunday or some other day is the first day of the week.

<kal-el data-firstday="$ { config.info.firstDay }">

We’ll be using template literals to render the markup. To format the dates for an international audience, we’ll use the Intl.DateTimeFormat API, again using the locale we specified earlier.

The month and year

When we call the month, we can set whether we want to use the long name (e.g. February) or the short name (e.g. Feb.). Let’s use the long name since it’s the title above the calendar:

<time datetime="$ {year}-$ {(pad(month))}">   $ {new Intl.DateTimeFormat(     locale,     { month:'long'}).format(date)} <i>$ {year}</i> </time>

Weekday names

For weekdays displayed above the grid of dates, we need both the long (e.g. “Sunday”) and short (abbreviated, ie. “Sun”) names. This way, we can use the “short” name when the calendar is short on space:

Intl.DateTimeFormat([locale], { weekday: 'long' }) Intl.DateTimeFormat([locale], { weekday: 'short' })

Let’s make a small helper method that makes it a little easier to call each one:

const weekdays = (firstDay, locale) => {   const date = new Date(0);   const arr = [...Array(7).keys()].map(i => {     date.setDate(5 + i)     return {       long: new Intl.DateTimeFormat([locale], { weekday: 'long'}).format(date),       short: new Intl.DateTimeFormat([locale], { weekday: 'short'}).format(date)     }   })   for (let i = 0; i < 8 - firstDay; i++) arr.splice(0, 0, arr.pop());   return arr; }

Here’s how we invoke that in the template:

<ol>   $ {weekdays(config.info.firstDay,locale).map(name => `     <li>       <abbr title="$ {name.long}">$ {name.short}</abbr>     </li>`).join('')   } </ol>

Day numbers

And finally, the days, wrapped in an <ol> element:

$ {[...Array(numOfDays).keys()].map(i => {   const cur = new Date(year, month, i + 1);   let day = cur.getDay(); if (day === 0) day = 7;   const today = renderToday && (config.today.day === i + 1) ? ' data-today':'';   return `     <li data-day="$ {day}"$ {today}$ {i === 0 || day === config.info.firstDay ? ` data-weeknumber="$ {new Intl.NumberFormat(locale).format(getWeek(cur))}"`:''}$ {config.info.weekend.includes(day) ? ` data-weekend`:''}>       <time datetime="$ {year}-$ {(pad(month))}-$ {pad(i)}" tabindex="0">         $ {new Intl.NumberFormat(locale).format(i + 1)}       </time>     </li>` }).join('')}

Let’s break that down:

  1. We create a “dummy” array, based on the “number of days” variable, which we’ll use to iterate.
  2. We create a day variable for the current day in the iteration.
  3. We fix the discrepancy between the Intl.Locale API and getDay().
  4. If the day is equal to today, we add a data-* attribute.
  5. Finally, we return the <li> element as a string with merged data.
  6. tabindex="0" makes the element focusable, when using keyboard navigation, after any positive tabindex values (Note: you should never add positive tabindex-values)

To “pad” the numbers in the datetime attribute, we use a little helper method:

const pad = (val) => (val + 1).toString().padStart(2, '0');

Week number

Again, the “week number” is where a week falls in a 52-week calendar. We use a little helper method for that as well:

function getWeek(cur) {   const date = new Date(cur.getTime());   date.setHours(0, 0, 0, 0);   date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);   const week = new Date(date.getFullYear(), 0, 4);   return 1 + Math.round(((date.getTime() - week.getTime()) / 86400000 - 3 + (week.getDay() + 6) % 7) / 7); }

I didn’t write this getWeek-method. It’s a cleaned up version of this script.

And that’s it! Thanks to the Intl.Locale, Intl.DateTimeFormat and Intl.NumberFormat APIs, we can now simply change the lang-attribute of the <html> element to change the context of the calendar based on the current region:

January 2023 calendar grid.
de-DE

January 2023 calendar grid.
fa-IR

January 2023 calendar grid.
zh-Hans-CN-u-nu-hanidec

Styling the calendar

You might recall how all the days are just one <ol> with list items. To style these into a readable calendar, we dive into the wonderful world of CSS Grid. In fact, we can repurpose the same grid from a starter calendar template right here on CSS-Tricks, but updated a smidge with the :is() relational pseudo to optimize the code.

Notice that I’m defining configurable CSS variables along the way (and prefixing them with ---kalel- to avoid conflicts).

kal-el :is(ol, ul) {   display: grid;   font-size: var(--kalel-fz, small);   grid-row-gap: var(--kalel-row-gap, .33em);   grid-template-columns: var(--kalel-gtc, repeat(7, 1fr));   list-style: none;   margin: unset;   padding: unset;   position: relative; }
Seven-column calendar grid with grid lines shown.

Let’s draw borders around the date numbers to help separate them visually:

kal-el :is(ol, ul) li {   border-color: var(--kalel-li-bdc, hsl(0, 0%, 80%));   border-style: var(--kalel-li-bds, solid);   border-width: var(--kalel-li-bdw, 0 0 1px 0);   grid-column: var(--kalel-li-gc, initial);   text-align: var(--kalel-li-tal, end);  }

The seven-column grid works fine when the first day of the month is also the first day of the week for the selected locale). But that’s the exception rather than the rule. Most times, we’ll need to shift the first day of the month to a different weekday.

Showing the first day of the month falling on a Thursday.

Remember all the extra data-* attributes we defined when writing our markup? We can hook into those to update which grid column (--kalel-li-gc) the first date number of the month is placed on:

[data-firstday="1"] [data-day="3"]:first-child {   --kalel-li-gc: 1 / 4; }

In this case, we’re spanning from the first grid column to the fourth grid column — which will automatically “push” the next item (Day 2) to the fifth grid column, and so forth.

Let’s add a little style to the “current” date, so it stands out. These are just my styles. You can totally do what you’d like here.

[data-today] {   --kalel-day-bdrs: 50%;   --kalel-day-bg: hsl(0, 86%, 40%);   --kalel-day-hover-bgc: hsl(0, 86%, 70%);   --kalel-day-c: #fff; }

I like the idea of styling the date numbers for weekends differently than weekdays. I’m going to use a reddish color to style those. Note that we can reach for the :not() pseudo-class to select them while leaving the current date alone:

[data-weekend]:not([data-today]) {    --kalel-day-c: var(--kalel-weekend-c, hsl(0, 86%, 46%)); }

Oh, and let’s not forget the week numbers that go before the first date number of each week. We used a data-weeknumber attribute in the markup for that, but the numbers won’t actually display unless we reveal them with CSS, which we can do on the ::before pseudo-element:

[data-weeknumber]::before {   display: var(--kalel-weeknumber-d, inline-block);   content: attr(data-weeknumber);   position: absolute;   inset-inline-start: 0;   /* additional styles */ }

We’re technically done at this point! We can render a calendar grid that shows the dates for the current month, complete with considerations for localizing the data by locale, and ensuring that the calendar uses proper semantics. And all we used was vanilla JavaScript and CSS!

But let’s take this one more step

Rendering an entire year

Maybe you need to display a full year of dates! So, rather than render the current month, you might want to display all of the month grids for the current year.

Well, the nice thing about the approach we’re using is that we can call the render method as many times as we want and merely change the integer that identifies the month on each instance. Let’s call it 12 times based on the current year.

as simple as calling the render-method 12 times, and just change the integer for monthi:

[...Array(12).keys()].map(i =>   render(     new Date(date.getFullYear(),     i,     date.getDate()),     config.locale,     date.getMonth()   ) ).join('')

It’s probably a good idea to create a new parent wrapper for the rendered year. Each calendar grid is a <kal-el> element. Let’s call the new parent wrapper <jor-el>, where Jor-El is the name of Kal-El’s father.

<jor-el id="app" data-year="true">   <kal-el data-firstday="7">     <!-- etc. -->   </kal-el>    <!-- other months --> </jor-el>

We can use <jor-el> to create a grid for our grids. So meta!

jor-el {   background: var(--jorel-bg, none);   display: var(--jorel-d, grid);   gap: var(--jorel-gap, 2.5rem);   grid-template-columns: var(--jorel-gtc, repeat(auto-fill, minmax(320px, 1fr)));   padding: var(--jorel-p, 0); }

Final demo

Bonus: Confetti Calendar

I read an excellent book called Making and Breaking the Grid the other day and stumbled on this beautiful “New Year’s poster”:

Source: Making and Breaking the Grid (2nd Edition) by Timothy Samara

I figured we could do something similar without changing anything in the HTML or JavaScript. I’ve taken the liberty to include full names for months, and numbers instead of day names, to make it more readable. Enjoy!


Making Calendars With Accessibility and Internationalization in Mind originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS-Tricks

, , , ,

5 Accessibility Quick Wins You Can Implement Today

Let’s face it: building an AA or AAA-accessible product can be quite daunting. Luckily, having an accessible product isn’t all-or-nothing. Even seemingly small improvements can have nice quality of life benefits for many people.

In that spirit, here are five accessibility quick wins you can implement today.

Quick Win 1: Indicate the Current Page

It’s probably safe to assume that a different style is the most common way to communicate the current page of a site or app. However, even if those styles are clear and with great contrast ratios, they’re still only a visual cue.

So what happens if a person with limited vision cannot see that separation? How will they know what page they’re on?

Creating an accessible product is to ensure its markup communicates as clearly as its design.

Adding aria-current="page" to the active navigation element is one way to ensure markup and design communicate the same information with or without assistive technologies.

<a aria-current="page" href="/">Home</a>

🎉 Bonus

Use CSS attribute selectors to style the aria-current="page" element to keep the visual and markup cues linked.

[aria-current="page"] {    /* Active element styles */ }

Quick Win 2: Document Language

While some people can visit a website and determine the language or locale of its content, not all people have that luxury. Again, markup must communicate the same information as the visual design — even if that information may seem implied.

Add the lang attribute to the <html> tag to communicate not only the document’s language, but its locale. This will help assistive technologies like screen readers understand and communicate the content. Even if the app only supports one language, this can be a nice quality of life improvement for many people.

<html lang="en-US">

For apps which support multiple languages, the <html> element is likely not the only one to need its lang value defined. Use the lang attribute on specific elements whose language differs from the rest of the document, like links within a language toggle menu. In this case, pair the use of lang with the hreflang attribute to not only communicate the language of the link itself, but also of its destination.

<a lang="fi" hreflang="fi" href="/" title="Suomeksi">   <bdi>Suomeksi</bdi> </a>

Quick Win 3: Use prefers-reduced-motion

Whether drawing attention to actions or updates, or creating a sense of life and charm, adding motion to an app can really elevate its experience. However, some people may find that experience disorienting.

Windows and MacOS both offer a setting at the OS level for people to greatly reduce the amount of motion when using their systems. The prefers-reduced-motion setting can greatly improve the experience on a computer, but it does not extends beyond the UI of the operating system. So wouldn’t it be nice if our apps could respect that same system setting and provide a more static experience for those who prefer it?

Well, with CSS media queries, they can.

The prefers-reduced-motion media query can be used to greatly reduce or remove all motion from an app whenever the system setting is enabled.

@media (prefers-reduced-motion: reduce) {   * {     animation-duration: 0.01ms !important;     animation-iteration-count: 1 !important;     transition-duration: 0.01ms !important;     scroll-behavior: auto !important;   } }

The blanket approach shown here prevents all motion, but it can leave little room for nuance. It’d be best to review the needs of those using the product, but consider these other options as well.

One approach could be to only animate one property at a time in prefers-reduced-motion settings. So consider a <Modal /> that fades and scales into view with opacity and transform. In reduced motion environments, only the opacity would transition. The scaling effect would be removed as they are more commonly problematic than fading.

Another option could be to look at the prefers-reduced-motion environment a bit more literally and remove all motion. This would do away with our scaling modals, sliding drawers, and bouncing notifications, but would still leave room for color transitions on links and buttons.

Quick Win 4: Indicate Data Sorting State

A common theme across all of these tips is to ensure that an app’s visual design and markup communicate the same things. So, when the design uses an arrow to indicate the sort direction of a table column, how can that also be communicated in the markup?

Setting the aria-sort attribute to ascending /descending on the header of the actively-sorted column allows the markup to communicate the same content structure as a visual indicator in the UI.

This will help ensure that people using assistive technologies and those who aren’t can understand the content in the same way.

<thead>   <tr>     <th>First Name</th>     <th aria-sort="ascending">Last Name</th>   </tr> </thead>

Quick Win 5: Lazy Loading Lists

Whether scrolling through an endless stream of tweets or through an impossible-to-decide list of products, the web has fully embraced lazy loading long lists of data (and alliteration, apparently).

This is when the aria-setsize and aria-posinset attributes become very valuable. While a person’s progression through the list can be communicated visually in many different ways, these attributes are used to communicate that same progression to many assistive technologies.

As developers, we likely have access to the length of an entire list as well as the index of the current items being displayed. With that, the aria-setsize attribute would define the total length of the list, while the aria-posinset attribute would define an item’s specific position (or index) within that list.

If the total length of the list is not known, then aria-setsize should be set to -1.

With these attributes, assistive technologies can better interpret a list and a person can better understand their position within it.

<h2 id="top-artists-title">Top Artists of 2021</h2> <ul role="listbox" aria-labelledby="top-artists-title">   <li role="option" aria-setsize="20" aria-posinset="5">Bloodbound</li>   <li role="option" aria-setsize="20" aria-posinset="6">Manimal</li>   <li role="option" aria-setsize="20" aria-posinset="7">Powerwolf</li> </ul>

Take a listen to how these attributes are announced using MacOS VoiceOver.

🎉 Bonus Win: Axe-DevTools Extension

Implementing those five accessibility quick wins is a great start, but that’s exactly what it is —a start. There’s a sprawling landscape of assistive technologies and sets of abilities a person can posses, and navigating it all alone can feel overwhelming.

Fortunately, there are plenty of tools to help with auditing a product’s accessibility that make the journey much more manageable. My personal favorite — my trusty accessibility compass — is the Axe-DevTools browser extension.

Running the Axe-DevTools accessibility scanner can return tons of valuable information. Not only will it display all issues and warnings found on the page, but it groups them by approximate severity. It can also highlight the element on the page or in the Elements tab and provide links to learn more about the specific issue.

However, most importantly, it will offer clear and concise approaches to fix the specific issue.

A screenshot of an Axe-DevTools accessibility report. Using the extension can lead to accessibility quick wins.

Wrapping Up

A product isn’t made accessible overnight; nor is a product’s accessibility work ever really complete. Like anything else on the web, accessibility evolves and requires maintenance. However, even seemingly small additions can have an impact on a product’s accessibility and a person’s overall experience.

After stepping into a new codebase, these are often some of the first few things I look into — some “low-hanging fruit” of accessibility, if you will.

Reaching AAA or even AA conformance can feel like scaling an 8,000 meter peak. These steps won’t carry you to the summit, but an expedition is never completed in a single stride.

Resources


5 Accessibility Quick Wins You Can Implement Today originally published on CSS-Tricks. You should get the newsletter.

CSS-Tricks

, , , ,
[Top]

Why Don’t Developers Take Accessibility Seriously?

You know that joke, “Two front-end developers walk into a bar and find they have nothing in common”? It’s funny, yet frustrating, because it’s true.

This article will present three different perspectives on accessibility in web design and development. Three perspectives that could help us bridge the great divide between users and designers/developers. It might help us find the common ground to building a better web and a better future.

The corner of a white and blue building in focus, with white on the left and blue on the right representing the divide between developers when it comes to accessibility practices.
Photo by Alexander Naglestad on Unsplash

Act 1

“I just don’t know how developers don’t think about accessibility.”

Someone once said that to me. Let’s stop and think about it for a minute. Maybe there’s a perspective to be had.

Think about how many things you have to know as a developer to successfully build a website. In any given day, for any given job position in web development, there are the other details of web development that come up. Meaning, it’s more than “just” knowing HTML, CSS, ARIA, and JavaScript. Developers will also learn other things over the course of their careers, based on what they need to do.

This could be package management, workspaces, code generators, collaboration tools, asset loading, asset management, CDN optimizations, bundle optimizations, unit tests, integration tests, visual regression tests, browser integration tests, code reviews, linting, formatting, communication through examples, changelogs, documentation, semantic versioning, security, app deployment, package releases, rollbacks, incremental improvements, incremental testing, continuous deployments, merge management, user experience, user interaction design, typography scales, aspect ratios for responsive design, data management, and… well, the list could go on, but you get the idea.

As a developer, I consider myself to be pretty gosh darn smart for knowing how to do most these things! Stop and consider this: if you think about how many people are in the world, and compare that to how many people in the world can build websites, it’s proportionally a very small percentage. That’s kind of… cool. Incredible, even. On top of that, think about the last time you shipped code and how good that felt. “I figured out a hard thing and made it work! Ahhhhh! I feel amazing!”

That kind of emotional high is pretty great, isn’t it? It makes me smile just to think about it.

Now, imagine that an accessibility subject-matter expert comes along and essentially tells you that not only are you not particularly smart, but you have been doing things wrong for a long time.

Ouch. Suddenly you don’t feel very good. Wrong? Me?? What??? Your adrenaline can even kick in and you start to feel defensive. Time to stick up for yourself… right? Time to dig those heels.

The cognitive dissonance can even be really overwhelming. It feels bad to find out that not only are you not good at the thing you thought you were really good at doing, but you’ve also been saying, “Screw you, who cares about you anyway,” to a whole bunch of people who can’t use the websites you’ve helped build because you (accidentally or otherwise) ignored that they even existed, that you ignored users who needed something more than the cleverness you were delivering for all these years. Ow.

All things considered, it is quite understandable to me that a developer would want to put their fingers in their ears and pretend that none of this has happened at all, that they are still very clever and awesome. That the one “expert” telling you that you did it wrong is just one person. And one person is easy to ignore.

end scene.

Act 2

“I feel like I don’t matter at all.”

This is a common refrain I hear from people who need assistive technology to use websites, but often find them unusable for any number of reasons. Maybe they can’t read the text because the website’s design has ignored color contrast. Maybe there are nested interactive elements, so they can’t even log in to do things like pay a utility bill or buy essential items on their own. Maybe their favorite singer has finally set up an online shop but the user with assistive technology cannot even navigate the site because, while it might look interactive from a sighted-user’s perspective, all the buttons are divs and are not interactive with a keyboard… at all.

This frustration can boil over and spill out; the brunt of this frustration is often borne by the folks who are trying to deliver more inclusive products. The result is a negative feedback cycle; some tech folks opt out of listening because “it’s rude” (and completely missing the irony of that statement). Other tech folks struggle with the emotional weight that so often accompanies working in accessibility-focused design and development.

The thing is, these users have been ignored for so long that it can feel like they are screaming into a void. Isn’t anyone listening? Doesn’t anyone care? It seems like the only way to even be acknowledged is to demand the treatment that the law affords them! Even then, they often feel ignored and forgotten. Are lawsuits the only recourse?

It increasingly seems that being loud and militant is the only way to be heard, and even then it might be a long time before anything happens.

end scene.

Act 3

“I know it doesn’t pass color contrast, but I feel like it’s just so restrictive on my creativity as a designer. I don’t like the way this looks, at all.”

I’ve heard this a lot across the span of my career. To some, inclusive design is not the necessary guardrail to ensure that our websites can be used by all, but rather a dampener on their creative freedom.

If you are a designer who thinks this way, please consider this: you’re not designing for yourself. This is not like physical art; while your visual design can be artistic, it’s still on the web. It’s still for the web. Web designers have a higher challenge—their artistic vision needs to be usable by everyone. Challenge yourself to move the conversation into a different space: you just haven’t found the right design yet. It’s a false choice to think that a design can either be beautiful or accessible; don’t fall into that trap.

end scene.

Let’s re-frame the conversation

These are just three of the perspectives we could consider when it comes to digital accessibility.

We could talk about the project manager that “just wants to ship features” and says that “we can come back to accessibility later.” We could talk about the developer who jokes that “they wouldn’t use the internet if they were blind anyway,” or the one that says they will only pay attention to accessibility “once browsers make them do it.”

We could, but we don’t really need to. We know how these these conversations go, because many of us have lived these experiences. The project never gets retrofitted. The company pays once to develop the product, then pays for an accessibility audit, then pays for the re-write after the audit shows that a retrofit is going to be more costly than building something new. We know the developer who insists they should only be forced to do something if the browser otherwise disallows it, and that they are unlikely to be convinced that the inclusive architecture of their code is not only beneficial, but necessary.

So what should we be talking about, then?

We need to acknowledge that designers and developers need to be learning about accessibility much sooner in their careers. I think of it with this analogy: Imagine you’ve learned a foreign language, but you only learned that language’s slang. Your words are technically correct, but there are a lot of native speakers of that language who will never be able to understand you. JavaScript-first web developers are often technically correct from a JavaScript perspective, but they also frequently create solutions that leave out a whole lotta people in the end.

How do we correct for this? I’m going to be resolute here, as we all must be. We need to make sure that any documentation we produce includes accessible code samples. Designs must contain accessible annotations. Our conference talks must include accessibility. The cool fun toys we make to make our lives easier? They must be accessible, and there must be no excuse for anything less This becomes our new minimum-viable product for anything related to the web.

But what about the code that already exists? What about the thousands of articles already written, talks already given, libraries already produced? How do we get past that? Even as I write this article for CSS-Tricks, I think about all of the articles I’ve read and the disappointment I’ve felt when I knew the end result was inaccessible. Or the really fun code-generating tools that don’t produce accessible code. Or the popular CSS frameworks that fail to consider tab order or color contrast. Do I want all of those people to feel bad, or be punished somehow?

Nope. Not even remotely. Nothing good comes from that kind of thinking. The good comes from the places we already know—compassion and curiosity.

We approach this with compassion and curiosity, because these are sustainable ways to improve. We will never improve if we wallow in the guilt of past actions, berating ourselves or others for ignoring accessibility for all these years. Frankly, we wouldn’t get anything done if we had to somehow pay for past ignorant actions; because yes, we did ignore it. In many ways, we still do ignore it.

Real examples: the Google Developer training teaches a lot of things, but it doesn’t teach anything more than the super basic parts of accessibility. JavaScript frameworks get so caught up in the cleverness and complexity of JavaScript that they completely forget that HTML already exists. Even then, accessibility can still take a back seat. Ember existed for about eight years before adding an accessibility-focused community group (even if they have made a lot of progress since then). React had to have a completely different router solution created. Vue hasn’t even begun to publicly address accessibility in the core framework (although there are community efforts). Accessibility engineers have been begging for inert to be implemented in browsers natively, but it often is underfunded and de-prioritized.

But we are technologists and artists, so our curiosity wins when we read interesting articles about how the accessibility object model and how our code can be translated by operating systems and fed into assistive technology. That’s pretty cool. After all, writing machine code so it can talk to another machine is probably more of what we imagined we’d be doing, right?

The thing is, we can only start to be compassionate toward other people once we are able to be compassionate toward ourselves. Sure, we messed up—but we don’t have to stay ignorant. Think about that time you debugged your code for hours and hours and it ended up being a typo or a missing semicolon. Do you still beat yourself up over that? No, you developed compassion through logical thinking. Think about the junior developer that started to be discouraged, and how you motivated them to keep trying and that we all have good days and bad. That’s compassion.

Here’s the cool part: not only do we have the technology, we are literally the ones that can fix it. We can get up and try to do better tomorrow. We can make some time to read about accessibility, and keep reading about it every day until we know it just as well as we do other things. It will be hard at first, just like the first time we tried… writing tests. Writing CSS. Working with that one API that is forever burned in our memory. But with repetition and practice, we got better. It got easier.

Logically, we know we can learn hard things; we have already learned hard things, time and time again. This is the life and the career we signed up for. This is what gets us out of bed every morning. We love challenges and we love figuring them out. We are totally here for this.

What can we do? Here are some action steps.

Perhaps I have lost some readers by this point. But, if you’ve gotten this far, maybe you’re asking, “Melanie, you’ve convinced me, but what can I do right now?” I will give you two lists to empower you to take action by giving you a place to start.

Compassionately improve yourself:

  1. Start following some folks with disabilities who are on social media with the goal of learning from their experiences. Listen to what they have to say. Don’t argue with them. Don’t tone police them. Listen to what they are trying to tell you. Maybe it won’t always come out in the way you’d prefer, but listen anyway.
  2. Retro-fit your knowledge. Try to start writing your next component with HTML first, then add functionality with JavaScript. Learn what you get for free from HTML and the browser. Take some courses that are focused on accessibility for engineers. Invest in your own improvement for the sake of improving your craft.
  3. Turn on a screen reader. Learn how it works. Figure out the settings—how do you turn on a text-only version? How do you change the voice? How do you make it stop talking, or make it talk faster? How do you browse by headings? How do you get a list of links? What are the keyboard shortcuts?

Bonus Challenge: Try your hand at building some accessibility-related tooling. Check out A11y Automation Tracker, an open source project that intends to track what automation could exist, but just hasn’t been created yet.

Incrementally improve your code

There are critical blockers that stop people from using your website. Don’t stop and feel bad about them; propel yourself into action and make your code even better than it was before.

Here are some of the worst ones:

  1. Nested interactive elements. Like putting a button inside of a link. Or another button inside of a button.
  2. Missing labels on input fields (or non-associated labels)
  3. Keyboard traps stop your users in their tracks. Learn what they are and how to avoid them.
  4. Are the images on your site important for users? Do they have the alt attribute with a meaningful value?
  5. Are there empty links on your site? Did you use a link when you should have used a button?

Suggestion: Read through the checklist on The A11y Project. It’s by no means exhaustive, but it will get you started.

And you know what? A good place to start is exactly where you are. A good time to start? Today.


Featured header photo by Scott Rodgerson on Unsplash


Why Don’t Developers Take Accessibility Seriously? originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , , ,
[Top]

8 Helpful Accessibility Links for January 2022

Every now and then, I find that I’ve accumulated a bunch of links about various things I find interesting. Accessibility is one of those things! Here’s a list of related links to other articles that I’ve been saving up and think are worth sharing.

Screenshot of the Accessibility Maze homepage.

8 Helpful Accessibility Links for January 2022 originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , , ,
[Top]

Some Articles About Accessibility I’ve Saved Recently IV


The post Some Articles About Accessibility I’ve Saved Recently IV appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,
[Top]

Some Articles About Accessibility I’ve Saved Recently III

  • The perfect link — Rian Rietveld defines them: “When you click on them, they take you somewhere else.” Not much code in here (we’ve got that), just a lot of practical accessibility advice. For example, the alt text for a linked image can allude to the fact that it is a link. Just an image: “A cherry tree in full bloom.” Link: “Wikipedia on cherry blossoms.”
  • Google Announces Seismic Change to Docs — George Joeckel covers the unfolding news that Google Docs is going to be rendered in <canvas>, which feels like a massive WTF moment when it comes to accessibility. At one point, the vibe was that there would be a separate product for people with screen reader needs. Separate but equal isn’t a good situation. Looks like the <canvas> based rendering stuff is on hold for now, so community feedback FTW?
  • Don’t use custom CSS mouse cursors — Eric Bailey: I believe that letting CSS load a custom cursor was a mistake.
  • Web Designers Grapple With Downside to Flashy Animation: Motion Sickness — Katie Deighton covers the idea that things like preference toggles and prefers-reduced-motion exists (although not by name). Always interesting to see a topic like this makes its way to a major publication like The Wall Street Journal.
  • prefers-reduced-motion and browser defaults — Speaking of prefers-reduced-motion, Bruce Lawson on the paragraph-of-the-year: Yes, it was a meeting request from Marketing to discuss a new product page with animations that are triggered on scroll. Much as a priest grasps his crucifix when facing a vampire, I immediately reached for Intersection Observer to avoid the browser grinding to a halt when watching to see if something is scrolled into view. And, like an exoricst sprinkling holy water on a demon, I also cleansed the code with a prefers-reduced-motion media query.
  • Using CSS to Enforce Accessibility — Adrian Roselli covers this great tactic where you don’t get the proper CSS styling unless you’ve also implemented the appropriate accessibility attributes (e.g. [role="region"][aria-labelledby][tabindex] for a scrolling table). This is a powerful idea and happens to showcase the power of CSS nicely in a way that styling solutions that avoid using selectors don’t benefit from.
  • Accessibility testing with Storybook — Varun Vachhar covers how you can run Axe over your component library even as you code. Accessibility issues, like color contrast problems, are bugs. Might as well give yourself the same tooling opportunities to fix them at the same time you’d fix any other bug.
  • Making A Strong Case For Accessibility — Todd Libby covers how you can fight for accessibility at work. Attempt 1.) Ethical. Attempt 2.) Financial. Attempt 3.) Legal. 4.) Humanization. Attempt 5.) Don’t ask, just do it.
  • Your Image Is Probably Not Decorative — Eric Bailey covers that most images with an empty alt attribute (literally alt="", no space) probably should have had one, and that when and alt description isn’t available, there are other options (e.g. make it available as an inline image (spacer.gif) even if it isn’t one otherwise, <title> in SVG, etc.).
  • Writing great alt text: Emotion matters — Annnnd speaking of alt, Jake Archibald learns from a 2011 Léonie Watson article: The relevant parts of an image aren’t limited to the cold hard facts.

The post Some Articles About Accessibility I’ve Saved Recently III appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,
[Top]

Links on Accessibility

  • Show/Hide password accessibility and password hints tutorial — Nicolas Steenhout goes deep on <input type="password"> accessibility. For one thing, being able to toggle it to type="text" should be possible, while announcing, politely, the change. But also, put the password hints (for choosing a password) before the input and programmatically connect them. And a bunch of other stuff. (Video version)
  • Practical accessibility, part 2: Name (almost) everything — Maggie Wachs explains how it’s all about the ability to move about the page.
  • Modern CSS Upgrades To Improve Accessibility — Stephanie Eckles shows off :focus-visible, outline-offset, order and other properties that can both help and hurt accessibility. My favorite are the clever uses of min() and max() which do things like reduce excessive margin on page zoom and maintain tappable area sizes.
  • WebAIM Million – 2021 Update — Jared Smith notes that things are getting better, even if just a bit. Is that the first time ever?! Things certainly aren’t “good” but it’s an encouraging trend.
  • Shift further left with Deque’s axe-linter for VS Code — Jonathan Thickens intros this new editor plugin which calls out errors just as if they were syntax, spelling, or formatting errors. As it should be! I’m using it and it works great. “Shift left” means “test earlier in the process” and “as you code” is about as early as it gets.
  • Content-visibility and Accessible Semantics — Marcy Sutton notes that the accessibility issues that hurt content-visibility when it first rolled have been resolved. This is the original blog post that documented what they were.
  • More Accessible Skeletons — Adrian Roselli notes that aria-busy="true" for a bit of skeleton HTML isn’t enough, as there is a little more attribute-shuffling to do, paired with CSS selectors to hide what needs to be hidden. (Demo)
  • Giving a damn about accessibility — Sheri Byrne-Haber’s “candid and practical handbook for designers.” Free digital (and audio) book. 📒

The post Links on Accessibility appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Some Articles About Accessibility I’ve Saved Recently

  • “Good news about display: contents and Chrome” — Rachel Andrew notes that the accessibility danger of using display: contents; is fixed in Chrome. The problem was that, say you had a parent div that is laid out as a grid and inside you have a <ul> with <li> elements, and you wanted the <li> elements to participate on that same parent grid. We have subgrid, but it’s not really the same thing. What you want is just to pretend like the <ul> isn’t there at all and that the <li> elements can hang out on the grid like anything else. The problem was that if you did that, you wiped out the accessible semantics of the list. But no more!
  • “Grid, content re-ordering, and accessibility”  — Speaking of grids and accessibility, here’s Rachel again teaching us (through this slide deck) how it’s all-too-easy to really diverge the source order and display order of content with modern layout techniques. At the moment, the solution is essentially not to do that, but the future might hold a way for browsers to update tab order to be visually sensible when you do dramatically alter the layout.
  • “The most useful accessibility testing tools and techniques” — Atrem Sapegin lists out some good ones, like eslint-plugin-jsx-a11y, storybook-addon-a11y, cypress-axe, Contrast app, Spectrum browser extension, and… using your tab key (lolz).
  • ButtonBuddy — Tool from Stephanie Eckles that helps generate CSS for buttons. But the real point of it is to give you colors as custom properties that satisfy color contrast guidelines.
  • “Are your Anchor Links Accessible?” — Amber Wilson goes through five iterations of an anchor link in/by a header before landing on a good one and, even then, there are questions to tackle.
  • “Don’t put pointer-events: none on form labels” — I’m a little shocked that anyone would do this at all, but it turns out it comes from Material Design’s “floating label” pattern. I think that pattern is so silly. It doesn’t actually save any space because you need the space where you float the label to anyway. Gosh.
  • “Accessible Text Labels For All” — Sara Soueidan tests real accessibility software and how it presents common interactive elements. For example, a “read more” link isn’t very useful (read more what?), and “add to cart” isn’t very useful alone (add what to cart?). You can add, for example, product names to those “add to cart” buttons, but don’t do it in the middle of the button as that can break things. Add the extra text at the end.

The post Some Articles About Accessibility I’ve Saved Recently appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,
[Top]

How We Improved the Accessibility of Our Single Page App Menu

I recently started working on a Progressive Web App (PWA) for a client with my team. We’re using React with client-side routing via React Router, and one of the first elements that we made was the main menu. Menus are a key component of any site or app. That’s really how folks get around, so making it accessible was a super high priority for the team.

But in the process, we learned that making an accessible main menu in a PWA isn’t as obvious as it might sound. I thought I’d share some of those lessons with you and how we overcame them.

As far as requirements go, we wanted a menu that users could not only navigate using a mouse, but using a keyboard as well, the acceptance criteria being that a user should be able to tab through the top-level menu items, and the sub-menu items that would otherwise only be visible if a user with a mouse hovered over a top-level menu item. And, of course, we wanted a focus ring to follow the elements that have focus.

The first thing we had to do was update the existing CSS that was set up to reveal a sub-menu when a top-level menu item is hovered. We were previously using the visibility property, changing between visible and hidden on the parent container’s hovered state. This works fine for mouse users, but for keyboard users, focus doesn’t automatically move to an element that is set to visibility: hidden (the same applies for elements that are given display: none). So we removed the visibility property, and instead used a very large negative position value:

.menu-item {   position: relative; }  .sub-menu {   position: absolute   left: -100000px; /* Kicking off  the page instead of hiding visiblity */ }  .menu-item:hover .sub-menu {   left: 0; }

This works perfectly fine for mouse users. But for keyboard users, the sub menu still wasn’t visible even though focus was within that sub menu! In order to make the sub-menu visible when an element within it has focus, we needed to make use of :focus and :focus-within on the parent container:

.menu-item {   position: relative; }  .sub-menu {   position: absolute   left: -100000px; }  .menu-item:hover .sub-menu, .menu-item:focus .sub-menu, .menu-item:focus-within .sub-menu {   left: 0; }

This updated code allows the the sub-menus to appear as each of the links within that menu gets focus. As soon as focus moves to the next sub menu, the first one hides, and the second becomes visible. Perfect! We considered this task complete, so a pull request was created and it was merged into the main branch.

But then we used the menu ourselves the next day in staging to create another page and ran into a problem. Upon selecting a menu item—regardless of whether it’s a click or a tab—the menu itself wouldn’t hide. Mouse users would have to click off to the side in some white space to clear the focus, and keyboard users were completely stuck! They couldn’t hit the esc key to clear focus, nor any other key combination. Instead, keyboard users would have to press the tab key enough times to move the focus through the menu and onto another element that didn’t cause a large drop down to obscure their view.

The reason the menu would stay visible is because the selected menu item retained focus. Client-side routing in a Single Page Application (SPA) means that only a part of the page will update; there isn’t a full page reload.

There was another issue we noticed: it was difficult for a keyboard user to use our “Jump to Content” link. Web users typically expect that pressing the tab key once will highlight a “Jump to Content” link, but our menu implementation broke that. We had to come up with a pattern to effectively replicate the “focus clearing” that browsers would otherwise give us for free on a full page reload.

The first option we tried was the easiest: Add an onClick prop to React Router’s Link component, calling document.activeElement.blur() when a link in the menu is selected:

const Menu = () => {   const clearFocus = () => {     document.activeElement.blur();   }    return (     <ul className="menu">       <li className="menu-item">         <Link to="/" onClick={clearFocus}>Home</Link>       </li>       <li className="menu-item">         <Link to="/products" onClick={clearFocus}>Products</Link>         <ul className="sub-menu">           <li>             <Link to="/products/tops" onClick={clearFocus}>Tops</Link>           </li>           <li>             <Link to="/products/bottoms" onClick={clearFocus}>Bottoms</Link>           </li>           <li>             <Link to="/products/accessories" onClick={clearFocus}>Accessories</Link>           </li>         </ul>       </li>     </ul>   ); }

This approach worked well for “closing” the menu after an item is clicked. However, if a keyboard user pressed the tab key after selecting one of the menu links, then the next link would become focused. As mentioned earlier, pressing the tab key after a navigation event would ideally focus on the “Jump to Content” link first.

At this point, we knew we were going to have to programmatically force focus to another element, preferably one that’s high up in the DOM. That way, when a user starts tabbing after a navigation event, they’ll arrive at or near the top of the page, similiar to a full page reload, making it much easier to access the jump link.

We initially tried to force focus on the <body> element itself, but this didn’t work as the body isn’t something the user can interact with. There wasn’t a way for it to receive focus.

The next idea was to force focus on the logo in the header, as this itself is just a link back to the home page and can receive focus. However, in this particular case, the logo was below the “Jump To Content” link in the DOM, which means that a user would have to shift + tab to get to it. No good.

We finally decided that we had to render an interact-able element, for example, an anchor element, in the DOM, at a point that’s above than the “Jump to Content” link. This new anchor element would be styled so that it’s invisible and that users are unable to focus on it using “normal” web interactions (i.e. it’s taken out of the normal tab flow). When a user selects a menu item, focus would be programmatically forced to this new anchor element, which means that pressing tab again would focus directly on the “Jump to Content” link. It also meant that the sub-menu would immediately hide itself once a menu item is selected.

const App = () => {   const focusResetRef = React.useRef();    const handleResetFocus = () => {     focusResetRef.current.focus();   };    return (     <Fragment>       <a         ref={focusResetRef}         href="javascript:void(0)"         tabIndex="-1"         style={{ position: "fixed", top: "-10000px" }}         aria-hidden       >Focus Reset</a>       <a href="#main" className="jump-to-content-a11y-styles">Jump To Content</a>       <Menu onSelectMenuItem={handleResetFocus} />       ...     </Fragment>   ) }

Some notes of this new “Focus Reset” anchor element:

  • href is set to javascript:void(0) so that if a user manages to interact with the element, nothing actually happens. For example, if a user presses the return key immediately after selecting a menu item, that will trigger the interaction. In that instance, we don’t want the page to do anything, or the URL to change.
  • tabIndex is set to -1 so that a user can’t “normally” move focus to this element. It also means that the first time a user presses the tab key upon loading a page, this element won’t be focused, but the “Jump To Content” link instead.
  • style simply moves the element out of the viewport. Setting to position: fixed ensures it’s taken out of the document flow, so there isn’t any vertical space allocated to the element
  • aria-hidden tells screen readers that this element isn’t important, so don’t announce it to users

But we figured we could improve this even further! Let’s imagine we have a mega menu, and the menu doesn’t hide automatically when a mouse user clicks a link. That’s going to cause frustration. A user will have to precisely move their mouse to a section of the page that doesn’t contain the menu in order to clear the :hover state, and therefore allow the menu to close.

What we need is to “force clear” the hover state. We can do that with the help of React and a clearHover class:

// Menu.jsx const Menu = (props) => {   const { onSelectMenuItem } = props;   const [clearHover, setClearHover] = React.useState(false);    const closeMenu= () => {     onSelectMenuItem();     setClearHover(true);   }    React.useEffect(() => {     let timeout;     if (clearHover) {       timeout = setTimeout(() => {         setClearHover(false);       }, 0); // Adjust this timeout to suit the applications' needs     }     return () => clearTimeout(timeout);   }, [clearHover]);    return (     <ul className={`menu $  {clearHover ? "clearHover" : ""}`}>       <li className="menu-item">         <Link to="/" onClick={closeMenu}>Home</Link>       </li>       <li className="menu-item">         <Link to="/products" onClick={closeMenu}>Products</Link>         <ul className="sub-menu">           {/* Sub Menu Items */}         </ul>       </li>     </ul>   ); }

This updated code hides the menu immediately when a menu item is clicked. It also hides immediately when a keyboard user selects a menu item. Pressing the tab key after selecting a navigation link moves the focus to the “Jump to Content” link.

At this point, our team had updated the menu component to a point where we were super happy. Both keyboard and mouse users get a consistent experience, and that experience follows what a browser does by default for a full page reload.

Our actual implementation is slightly different than the example here so we could use the pattern on other projects. We put it into a React Context, with the Provider set to wrap the Header component, and the Focus Reset element being automatically added just before the Provider’s children. That way, the element is placed before the “Jump to Content” link in the DOM hierarchy. It also allows us to access the focus reset function with a simple hook, instead of having to prop drill it.

We have created a Code Sandbox that allows you to play with the three different solutions we covered here. You’ll definitely see the pain points of the earlier implementation, and then see how much better the end result feels!

We would love to hear feedback on this implementation! We think it’s going to work well, but it hasn’t been released to in the wild yet, so we don’t have definitive data or user feedback. We’re certainly not a11y experts, just doing our best with what we do know, and are very open and willing to learn more on the topic.


The post How We Improved the Accessibility of Our Single Page App Menu appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

Beautiful accessibility with Floating Focus

Imagine if your :focus styles animated from element to element as you tab through a site. Like the focus ring up and flew across the page to the next element. The spirit of it is similar to smooth scrolling: it’s easier to understand what is happening when movement accompanies the change¹. Rather than scrolling (or focus change) being an instant jump, movement guides you to the new location.

Guido Bouman thought this would be good for accessibility and looked at some options (e.g. Flying Focus) but ultimately created their own, Floating Focus:

After this exploration we had a good idea of what a good focus state needed. It needs to have a high contrast but not impair readability of the underlying components. It has to guide the user to the next focus target with a form of transition. And it only needs to show for users benefitting from the focus outline.

We’ve covered a similar thing before in 2019 when Maurice Mahan FocusOverlay.

Here’s what I wrote about it back then:

  • It’s a neat effect.
  • I can imagine it being an accessibility win since, while the page will scroll to make sure the next focused element is visible, it doesn’t otherwise help you see where that focus has gone. Movement that directs attention toward the next focused element may help make it more clear.
  • I can imagine it being harmful to accessibility in that it is motion that isn’t usually there and could be surprising and possibly offputting.
  • If it “just works” on all my focusable elements, that’s cool, but I see there are data attributes for controlling the behavior. If I find myself needing to sprinkle behavior control all over my templates to accommodate this specific library, I’d probably be less into it.

In that article, I covered a conditional loading idea for not loading this if prefers-reduced-motion was set to reduce. These days, you might do a conditional ES Modules import.

Don’t take any of this as advice that this movement-based focus stuff is 100% good for accessibility. I don’t feel qualified to make that determination. It is interesting though.

  1. This reminds me of “transitional interfaces” as well. Movement can really help make clear what is happening in a UI.

Direct Link to ArticlePermalink


The post Beautiful accessibility with Floating Focus appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]