Designing Better Links For Websites And Emails: A Guideline

Why are “click here” and “by this link” poor choices? And is it acceptable to use “read more”? All these phrases have become so common that many people don’t see any problems with them.

How many times have you encountered or composed the following on websites, in emails, or intranets?

  • Fill in this form by the end of the day.
  • Check the equipment policy by the link.
  • You can find more information here, here, and here.

In this article, I’ll explain popular wording and formatting mistakes and will show more accessible and informative alternatives. Let’s go!

Meaningful Links

So what exactly is a hyperlink? It’s a combination of a web address (URL) and a clickable element (oftentimes a word or phrase, sometimes an image). While this is a vast topic, we’ll focus on text links, namely their usability and accessibility.

Thoughtfully composed links express respect to readers, whereas jumbled-up ones cause confusion and suspicion. When a link is presented as “here” or “this,” it’s harder to aim it with a cursor or finger. Also, it lacks transparency. What is hidden behind it: a page or file, an article or web form? One should re-read the whole sentence or paragraph to guess.

On the contrary, URLs attached to concise self-explanatory phrases inform people about the destination and are more convenient targets for clicking or tapping. Moreover, a well-composed link makes sense out of context and typically combines a topic (e.g. security, brand, marketing) and format (questionnaire, request form, guideline, policy, and so on).

Exposing URLs

If a web address is short and doesn’t look like M$c0P88%X4LHr&dxQ1A, then exposing it right away will work quite well, too, especially if the audience is supposed to copy it and paste it somewhere else.

And if you’ve got a long indecipherable chain of symbols, exposing it isn’t a great idea in most situations. In this case, consider embedding a URL into a succinct phrase or shortening the address in tools like Bitly or Cuttly.

However, these tools aren’t silver bullets: you do get a shorter link, but its meaningful parts will be replaced with random symbols, which are suspicious and not informative. Customizing shortened URLs is possible, but it’s typically a paid feature.

Compare the following examples:

  • bit.ly/30SjUa4y (suspicious and unreadable);
  • bit.ly/smashing-books (readable topic);
  • smash.ing/30SjUa4y (recognizable domain);
  • smash.ing/books (fully transparent).

Download Links

A link that guides to some downloadable resource needs a slightly different treatment. Besides embedding it into a meaningful phrase, you should also inform users about the file format and size:

  • The format gives clues to what you can do with this data (e.g. if the information is read-only or editable);
  • The file size is crucial for people with costly internet, slow connection, or limited local storage.

When you share a bunch of files (let’s say in different formats or versions), it’s not enough to design each link correctly. The whole series should be well-scannable and easy to use.

Links vs. Buttons

Not all links on a page or in an email are equally important. Authors often want their audience to click on the primary link, whereas other links can be skipped. If you’re going to draw people’s attention to the main action, think of presenting it as a button:

  • “Subscribe to the newsletter”
  • “Buy tickets”
  • “Get the white paper”
  • “Download the recording”

If you cannot create a button because of technical or time constraints, go for a quick-and-dirty solution: put that link in a separate line, make it bold, add spacing above and below, and so on.

Of course, button text should follow corresponding patterns so that you don’t cross the line between motivating readers and manipulating them:

  • Be concise (up to 4–5 words);
  • Ideally, start with a verb (e.g. “get”, “buy”, “download”, “apply for”, and so on);
  • Call the action honestly (i.e. avoid hushing up unpleasant steps like watching ads, registration or submitting personal data).

Compare “Download the report,” which assumes that downloading starts immediately after clicking, and “Get the report,” when a user downloads the file in exchange for their name and contact details.

Link-Rich Texts

Links enable the functioning of the Internet, however, vigorously pumping URLs into each sentence isn’t a good practice (of course, unless you contribute to a Wikipedia-like knowledge base that is cross-connected by nature).

Step zero is to make sure you really—really—need all the links. If you can edit something out, there won’t be a problem to solve. Otherwise, try to group the links: as a bulleted list, on the side of related paragraphs, or under a suitable title (e.g. “Recommended materials” or “Resources”).

Grouping the links helps a lot, but if the goal is to trigger action, the primary link should stand out. So, why not make it a button, then?

In the previous sections, we figured out how descriptive links increase usability and accessibility. At the same time, such links are longer, and consequently, can appear divided in a paragraph, when the first part of a link remains at the end of the previous line, and the second part jumps to the next line. It seems trivial compared to bigger flaws, but distorted links are a bit annoying in link-crowded texts.

If a paragraph width is fixed, compose text the way all links fit into lines, for example, try to start a paragraph with a link. However, browsers and devices render content differently, and links will still shift for some users. That’s why lists are a safer option for a set of links.

Link Accessibility

Accessible links are not only the ones that look tidy and clear; they should also be properly working. Web Content Accessibility Guidelines (WCAG), the world’s most famous digital accessibility standard, includes recommendations about hyperlinks, including some non-visual features.

Distinction

One of the WCAG requirements is not to rely on color only when you want to distinguish a button or link from the rest of the text. Painting links in blue or another color doesn’t suffice since it still might not be visible for people with color blindness. The most typical method is underlining links; they can also appear in bold font.

Color Contrast

Links are essential interactive elements and have to comply with contrast recommendations. WCAG has two levels of contrast compliance:

  1. AA: medium, used by many websites for a mass audience;
  2. AAA: high, primarily applied on governmental sites and by communities of people with disabilities.

For example, the AA level requires maintaining a contrast between a link and background of at least 4.5:1 for normal font size and 3:1 for large text.

Note: You can always check your colors with the help of the online Contrast Checker or Figma’s Contrast plugin.

Focus State

Like all interactive components, links should have a visible keyboard focus. All popular browsers have an embedded accessible focus by default (you might have seen this bold blue frame around input fields, dropdowns, buttons, and links in Google Chrome). Unfortunately, on some sites, focus gets manually removed or visually customized so that a focused link can look even less noticeable (e.g. faded out).

Optimization For Screen Readers

Blind users don’t see the web — they listen to it by means of “screen readers,” assistive programs that transform a written text into fast robotic speech. They navigate with a keyboard and remember dozens of handy shortcuts to jump between headings, buttons, or links instead of obediently listening to the entire content on a page.

So, when you remove wordiness for sighted people (for example, in the lists of different language versions or formats), it’s important to keep links clear for screen reader users, too. Otherwise, blind visitors will hear the following:

“Ukrainian — link, English — link, German — link”

The self-explanatory should be heard instead:

“Download project plan template in Ukrainian — link, download project plan template in English — link…”

And probably the most annoying thing on a news website is to hear this:

“Read more — link, read more — link, read more — link”

Sighted people can guess that “Read more…” relates to the nearest title, and blind people need individualized read-mores. Fortunately, the HTML attribute aria-label comes in handy here; it enables attaching explanatory text for screen reader users.

It’s often a designer’s responsibility to compose accessibility-related text and collaborate with a developer around optimal implementation, so here is a simplified code example:

<h4>News</h4>
<p>Eleks Design Team will participate in the Space Hackathon.
<a href="aerospace-hackathon.html" aria-label="Read more about Eleks participation in the Space Hackathon">Read more...</a>
</p>
<p>Projector Tech and Creative Institute launches five courses on web accessibility this year.
<a href="new-courses.html" aria-label="Read more about new courses on accessibility by Projector Institute">Read more...</a>
</p>

As you can see, each “Read more” has an extended explanation for screen readers. However, you won’t need to take care of article links with aria-label if each title is a link itself.

<h4>News</h4>
<h5><a href="aerospace-hackathon.html">Eleks Design Team will participate in the Space Hackathon</a>
</h5>
<h5><a href="new-courses.html">Projector Tech and Creative Institute launches five courses on web accessibility this year</a>
</h5>

Duplicated links

Multiple identical links are yet another widespread controversial practice. For example, on a web page, it means that the same web address is attached to an article title, hero image, and intro sentence. At first glance, nothing’s wrong: wherever you click — you get to the article. But for blind users, it means repeating the same information thrice, which extends the time they need to sift through content to what they are interested in.

An important note: We are now talking about identical destinations, but a card can include different ones, for instance, a link to the article, author’s profile, and tags. In this case, minor links can appear “wrapped” in the main one.

Now, emails. Let’s say we have an invitation to some online event, where a Zoom link repeats several times. In the event description, “what/when/where” section, and closing part. Not only will it create an impression of mess for sighted users, but also visually impaired users will be troubled with jumping between duplicated links.

Recommended Reading

In this article, I wanted to suggest options instead of showing the topic in black and white. There are multiple shades of good design, and you can find yours on the overlap of best practices and your particular case. Meanwhile, some additional reading on this topic:

A Complete Guide To Accessible Front-End Components

Table of Contents

Below you’ll find an alphabetical list of all accessible components. Skip the table of contents, or just scroll down to explore them one-by-one.

Accessible Modals

You might have a simple modal or overlay on the page, perhaps to confirm customer’s input, or to show a couple of photos in a gallery, or just to confirm user’s preferences. In all these cases, building an accessible modal will turn out to become quite an adventure, also know as a focus trap.

As Eric Bailey explains in detail, you’ll need to identify the bounds of the trapped content, including the first and last focusable item, then remove everything that isn’t within it, move focus into the trapped content, listen for events that escape the boundary, restore previous state and move focus back to the interactive element that triggered the trapped content.

Ideally, we’d use something as simple as the dialog element in HTML, but unfortunately it has massive accessibility issues. With the Shadow DOM, managing focus isn’t easy either. We can use the inert attribute to remove, and then restore the ability of interactive elements to be discovered and focused. For older browsers, we can use inert polyfills from Google Chrome and from WICG.

  • Scott O'Hara's accessible-modal-window is a reliable fully accessible script to use.
  • Kitty Giraudel will shortly be releasing a11y-dialog Next, a lightweight (1.6 KB) script that traps and restores focus, toggles aria–* attributes and closes dialog on overlay click and Escape. It’s important not to confuse this version with previous version (6.1.0) as it relies on the <dialog> which which still lacks in implementation support, and has lingering accessibility issues.
  • You could look into Parvus, a simple, accessible, open-source image lightbox without dependencies. In a typical scenario, we’d have an image linked to larger version of the image. With Parvus,, it’s enough to add a class .lightbox to the link wrapping around an image, and the script does everything else for you automatically.

Accessible Tabs

Your interface might be using tab panels, but to keep the content of these tabs accessible to keyboard-users and screen reader users, we need a very careful and deliberate exposition of visual design and ARIA semantics. In Tabbed Interfaces, Heydon Pickering goes into detail trying to figure out just the right solution to respect keyboard behavior and focus management. He suggests to progressively enhance sections into tab panels (code example) (thanks to Daniela Kubesch for the tip!).

As Adam Silver notes, screen reader users who are less savvy may not know to use arrow keys to switch tabs. There’s an argument to make all the tabs focusable in the normal tab sequence with little intervention from developers to change the way tabs work via keyboard.

Alternatively, TabPanelWidget is a responsive and accessible solution for tabs. It relies on plain old semantic HTML, and turns into an accordion whenever the tabs cannot fit entirely (thanks to ResizeObserver but there’s a polyfill for browsers that don’t support it yet).

The script is not only a semantic and accessible solution, but also a responsive and versatile one to help you create Tabpanel and accordion widgets for the web. It is keyboard-friendly and available as a vanilla JS library (or as a widget for Vue, React and Angular).

Accessible Toggle Switches

Whenever our forms provide a binary selection to our customers — on/off, dark/light mode etc. — we could use a toggle switch. The switch needs to serve a couple of purposes: it needs to clearly explain the current selection (and that’s clear not that often at all!), it needs to explain that there are two options, and it needs to be obvious enough for customers to understand how to switch between them. When Sara Soueidan was looking into how to build a toggle switch, she of course spent quite a bit of time looking into how to build an accessible toggle switch.

Sara’s solution uses two radio buttons, each with its own label, announced to assistive technologies as a couple of separate options, accessible via keyboard, and has no additional ARIA or JS requirements to function. The outcome is a theme switching toggle code example, and you can also take a look at Scott O’Hara’s code example.

It’s important to note that Sara’s radio button toggle switch is accessible because of its two labels. So if a toggle switch does not have two labels, this would not be a pattern to use. You can find markup patterns for toggle switches in Scott’s repo. (thanks to Scott O'Hara for the tip!).

Accessible Autocomplete

Every time you have to deal with a larger data set, be it a map, a data visualization, or just a country selector in checkout, autocomplete can boost customer’s input massively. But just as it helps with the input, it needs to help with announcing the options and the selection to the screen reader users as well.

Gov.uk, the team behind the Government Digital Service in UK, has open-sourced accessible-autocomplete (among many other things), a JavaScript component that follows WAI-ARIA best practices. You can choose when to start displaying suggestions, and allows to display the menu as an absolutely positioned overlay, or select the first suggestion by default. The team also provides a demo page, with a dozen of accessible autocomplete examples and implementations.

Accessible Component Libraries

While many of the component libraries we create are trying to cover all the usual suspects (the accordions, the tables, the carousels, the drop-downs, along with typography, colors and box shadows), No Style Design System by Adam Silver is focused primarily around accessibility and web forms.

As a system created for and used in his book on Form Design Patterns, Adam’s library provides a set of accessible components for everything from autocomplete, checkboxes and password reveal to radios, select boxes and steppers. Most of them have a minimal CSS styling with clean, accessible markup.

And if you need slightly more advanced components, Heydon Pickering’s Inclusive Components — we mentioned some examples from it above — has got your back: with comprehensive tutorials on accessible cards, data tables, notifications, sliders, tabbed interfaces, tooltips, menus and toggles.

Accessible Data Visualizations

Data visualizations often contain important information that users have to act upon. While sometimes we can use large numbers with short sentences instead, visualizations can help understand developments and large amount of information faster. But that means that the information has to be easy to understand, and that refers especially to the selection of colors, the way information is presented, labels, legends as well as patterns and shapes. In their series of articles on accessibility in data visualizations, Sarah L. Fossheim highlights useful guidelines and resources around the topic, along with examples, do’s and don’ts to keep in mind when designing accessible data visualizations.

Sarah suggests to not rely on color to explain the data, and avoid bright and low-contrast colors in general. Using patterns and shapes in addition to color is useful, and clear language, labels and legends can help clearly explain the data visualization. Every article is packed with plenty of examples and resources for further reading. Also worth checking: Sarah’s review of US presidential election data visualizations (thanks to Stephanie Eckles for the tip!).

Accessible Color Systems

Getting color contrast right is an essential part of making sure that not only people with visual impairments can easily use your product but also everyone else when they are in low-light environments or using older screens. However, if you’ve ever tried to create an accessible color system yourself, you probably know that this can be quite a challenge.

The team at Stripe recently decided to tackle the challenge and redesign their existing color system. The benefits it should provide out of the box: pass accessibility guidelines, use clear and vibrant hues that users can easily distinguish from one another, and have a consistent visual weight without a color appearing to take priority over another. If you’re curious to find out more about their approach, their blog post on accessible color systems will give you valuable insights.

Accessible Color Palettes

Finding the perfect tint or shade of a color is not only a matter of taste but also accessibility. After all, if color contrast is lacking, a product could, in the worst case, even become unusable for people with vision impairments. WCAG 2.0 level AA requires a contrast ratio of at least 4.5:1 for normal text.) and 3:1 for large text, and WCAG 2.1 requires a contrast ratio of at least 3:1 for graphics and UI components (such as form input borders). AAA requires a contrast ratio of at least 7:1 for normal text and 4.5:1 for large text. A very detailed contrast checker to help you detect potential pitfalls ahead of time comes from Gianluca Gini: Geenes.

The tool lets you tinker with hue ranges and saturation and apply the color palettes to one of three selectable UI mockups. Once applied, you can trigger different kinds of vision impairments to see how affected people see the colors and, finally, make an informed decision on the best tones for your palette. To use the colors right away, just copy and paste their code or export them to Sketch. You can also emulate vision deficiencies in DevTools.

Accessible Date Pickers

There are dozens of date picker libraries out there, but it’s always great to have reliable workhorses that just work across browsers, don’t have heavy dependencies, are written reasonably well, and meet all major accessibility requirements.

Duet Date Picker is just like that. It’s an accessible, WCAG 2.1 compliant date picker that can be implemented and used across any JavaScript framework or no framework at all. It comes with built-in functionality that allows you to set a minimum and a maximum allowed date, and weighs around 10kb minified and Gzip’ed (this includes all styles and icons).

If you need an alternative, check out React Dates, a library released by Airbnb that’s optimized for internationalization, while also being accessible and mobile-friendly.

Accessible Data Charts

Data visualizations are a great way to make information stand out. However, they also come with their own accessibility challenges. When Sara Soueidan teamed up with SuperFriendly to create an accessible micro-site for Khan Academy’s annual report, she wanted to make sure that the way the data is presented and implemented is as accessible as possible, regardless of how a visitor explores the site. Her solution: SVG.

In a case study on accessible data charts, Sara summarized everything you need to consider when you want to make your SVG charts and visualizations accessible — beginning with the most important step of choosing an appropriate embedding technique. It also covers why you should avoid trying to make an SVG chart accessible using ARIA roles and why Sara didn’t choose <figure> to embed them. A fantastic reference guide. Plus: especially on graphs we could also use better accessible text labels, and Sara covers them in a separate article as well.

Accessible Icon Links and Buttons

It’s not uncommon to have a link or button that visually has no text but consists only of an icon — a compact navbar, for example, or social icons. But how do you make sure that these kinds of icon links are fully accessible? As it turns out, it’s not as straightforward as one might think.

To show how we can do better, Kitty Giraudel dedicated an article "Accessible Icon Links" to the issue. They use an icon link consisting of an SVG with the iconic Twitter bird to illustrate the point, and shows step by step how to make it accessible: with a descriptive text that is visually hidden, then removing the SVG markup from the accessibility tree with aria-hidden, and, finally, correcting the fact that svg elements can be focused on Internet Explorer by adding the focusable attribute. At the end of the article, Kitty also shows how to turn all of this into a little React component.

A small detail that will make a huge difference for a lot of users.

In Creating Accessible Icon Buttons and Inclusively Hidden, Sara Soueidan and Scott O’Hara go into all the fine intricacies and details of icon buttons, exploring a number of techniques to make it work. Sara and Scott explore a number techniques, suggesting to use an appropriate technique for accessible visually hidden text — the text that will be visually hidden but accessible to screen readers. This could be done with a .sr-only utility class, or hidden and aria-labelledby, or aria-label alone. Sara wouldn’t recommend to use the SVG icon itself to provide a label for the button when I can provide one on the button itself directly.

In general though, there is still quite a bit of confusion which element to use for user interaction: when do we use links, and when do we use buttons? Marcy Sutton has written a detailed piece on Links vs. Buttons in Modern Applications. With a link, the visitor navigates to a new resource, taking them away from the current context. But a button prompts a change in the interface.

Marcy outlines use cases for both links and buttons in single-page applications, showing that a button is a perfect element for opening a modal window, triggering a pop-up, toggling an interface or playing media content. You can also check Vadim Makeev’s article on “When Is A Button Not A Button?”.

Accessible Tooltips and Toggletips

A component that’s closely related to icon buttons is a tooltip. Literally "tips for tools", they are little pieces of information that explain the purpose of a control, or a visual, that otherwise could be misunderstood. Every time we want to explain why we need a particular piece of personal information in a checkout, that’s where we’ll probably be using a good old tooltip. So, how do we get them right?

Heydon Pickering’s Inclusive Tooltips and Toggletips provides a very thorough overview of pretty much everything needed to build an accessible tooltip. That means deciding whether the tip’s content should be provided as the label or description and choose ARIA properties accordingly, not relying on title attributes and avoiding putting interactive content such as close and confirm buttons or links in tooltips.

Accessible Footnotes and Sidenotes

In their essence, footnotes aren’t much more than jump-links — links to the description of a source, either placed at the bottom of the document, or in the sidebar, or appearing inline, a little accordion. However, as footnotes are jump-links, we need to ensure that screen reader users understand when links are references to footnotes — and we can do it with the aria-describedby attribute. The counter for every link would be implemented via a CSS counter. With :target, we then highlight the row which the reader has jumped to, and we provide a back-link back to the actual footnote place in the content.

Kitty Giraudel goes into detail explaining how to build accessible footnotes, and you can also check how to build accessible footnotes with React and use react-a11y-footnotes to build them in React with Eleventy (thanks to Kitty Giraudel for the tip!).

Accessible SVGs

Talking about SVGs: what we can do with SVGs today goes way beyond the basic shapes of yesteryear. Not only can we describe SVG icons, but also style and animate them. If true inclusiveness lies beyond patterns — what other factors should we consider when designing and developing accessible SVGs?

That’s exactly the question that Carie Fisher is answering in her piece on Accessible SVGs: Inclusiveness Beyond Patterns. In the article, Carie takes a closer look at SVG color and contrast, light and dark modes, SVG animation, reduced motion and plenty of tools focused all around accessibility. You’ll also find demos and code examples in the articles, along with detailed explanations and pointers for further reading.

And if you’d like to dive deeper into the complex world of accessible components — not only related to SVGs — we’ve just published Carie’s piece on accessible code patterns.

Better :focus Styles

Every browser has default focus styles, yet out of the box, they aren’t very accessible. The goal of :focus is to give the user guidance on where exactly they are in the document and help them navigate through it. To achieve that, we need to avoid a focus that’s too subtle or not visible at all. In fact, removing outline is a bad idea as it removes any visible indication of focus for keyboard users. The more obvious the focus is, the better.

There are ways of designing better :focus styles. In his article Tips For Focus Styles, Nic Chan highlights a few helpful tips on how to improve focus styles with better affordance and a bit of padding, offset, and proper outlines. Need more fun with :focus styles? Lari Maza has got your back, too.

We can also use :focus-within to style the parent element of a focused element, and :focus-visible to not show focus styles when interacting with a mouse/pointer  if it causes any issues in your design.

It’s important co consider the accessibility concerns around :focus-visible: as Hidde de Vries has noted, not all people who rely on focus styles use a keyboard and making focus styles keyboard-only takes away an affordance for mouse users too, as focus also indicates that something is interactive (thanks to Jason Webb for the tip!).

Finally, it’s worth noting that most recently Chrome, Edge, and other Chromium-based browsers stopped displaying a focus indicator (focus ring) when the user clicks or taps a button (thanks to Kim Johannesen for the tip!).

Accessible Cross-Browser Form Styles

Have you ever struggled with hiding and styling custom checkboxes and radio buttons? What about custom select-styles? Or perhaps an accessible dropdown-navigation menu? We tend to build and rebuild the same components all the time, so let’s get them right once and for all.

Sarah Higley’s “<select> your poison” is a comprehensive two-part deep dive into all the challenges and intricacies of styling the <select> element, with editable and multi-select variants, their comparative usability (with data!) and practical recommendations of how to get it right.

Stephanie Eckles’ Modern CSS Solutions for Old CSS Problems highlights plenty of useful modern techniques to solve plenty of challenges, but some articles from her series are dedicated to forms: CSS custom checkboxes, styled radio buttons, select styles, inputs, and textareas.

On her blog, Sara Soueidan goes into detail explaining how to inclusively hide and style checkboxes and radio buttons. Bonus: Adrian Roselli’s code examples provides additional insights into under-engineered toggles. Fantastic resources to use right away and style forms accessibly.

Accessible Checkboxes And Radio Buttons

The good ol’ issue: how do we style checkboxes and radio-buttons to ensure that they look, well, at least similar, in most browsers — while ensuring that they stay accessible as well? In her article, Sara Soueidan covers a few techniques to keep in mind to achieve the desired result.

Sara covers the different techniques for hiding elements, how each of them affects the accessibility of the content, and how to visually hide them, so they can be replaced with a more styleable alternative: the SVG.

When hiding an interactive element, we need to make sure we choose a hiding technique that keeps it screen reader-accessible, position it on top of whatever is visually replacing it, so that a user navigating by touch can find it where they expect to, and then make it transparent. Sara also provides live demos that we can use right away, along with useful references to articles for further reading.

Accessible Carousels and Content Sliders

An accessible carousel sounds a bit like oxymoron — while there are plenty of scripts that provide the functionality, only few of them are accessible. Now there are, of course, accessible range sliders, but carousels are a slightly different component. As Alison Walden notices in her article on “If you must use a carousel, make it accessible”, the sighted person is not forced to use the carousel at all, but keyboard users are forced to navigate through the carousel in its entirety. At the very least, a hidden “skip” link could appear on keyboard focus. Also, once the person has tabbed through all the panel sets, focus should move to the next interactive element that follows the carousel.

Heydon Pickering suggests to use list markup to group the slides together, include previous and next buttons, snap points, and use invisible linked items removed from focus. The article also provides a code sample which uses IntersectionObserver, so you might need a polyfill for it.

Accessible Tap/Click Menu

Is it still a good idea to design mega-drop-downs opening on hover? Probably not. Hover menus have plenty of usability and accessibility issues, as they are inconsistent, confusing and of course need an alternative solution for mobile devices. In fact, Mark Root-Wiley suggests that it’s about time to drop hover menus in favor of unambiguous and accessible click menus.

In his article, Mark goes into fine details of how to build an accessible click menu, along with useful pointers and references from his research. The idea is to start building the menu as a CSS-only hover menu that uses li:hover > ul and li:focus-within > ul to show the submenus. Then, we use JavaScript to create the <button> elements, set the aria attributes, and add the event handlers. The final result is available as a code example on CodePen and as a GitHub repo. This should be a good starting point for your menu as well.

Accessible “Skip” Links

Especially on pages with a large amount of navigation, moving between sections or around the page can be frustrating and annoying. That’s where “Skip” links can be very helpful. Unfortunately, it’s not uncommon to see “Skip” links being implemented but hidden away with display: none, and as such, unavailable to anybody (including screen reader users and keyboard users).

In How To Create a “Skip content” Link, Paul Ryan provides a step-by-step tutorial on how to implement an accessible skip content link. Basically we use CSS transform to push the skip link off the screen, but bring it back on screen on :focus. In the comments to the article, Eric Bailey also noticed that we could provide skip-links before sections of content that contain lots of interactive items, or items that can be tough to navigate through (such as table of contents and iframes).

Accessible Tables

There are plenty of accessibility issues related to tables, but the biggest challenges is to turn a visual representation into a linear series that will be read aloud meaningfully by a screen reader, without omitting any important information. Fortunately, Adrian Roselli has been spending a lot of time exploring the challenges and solutions of accessible tables.

In his post on accessible tables, Adrian suggests to wrap the table in a <div> with role="region", aria-labelledby and tabindex="0" to ensure that a keyboard-only user can tab to the container, that the table receives focus and <caption> within table to ensure that it’s properly announced to screen readers. Adrian also provides a code example for a responsive table, as well as tables with expandable rows, sortable table and fixed table headers.

How Screen Readers Navigate Data Tables

Have you ever tried to navigate a table with a screen reader? If not, you should check out Leonie Watson’s article on how screen readers navigate data tables. It shares precious insights and shows what matters to create frustration-free tables that can be used by anyone.

In the post, Leonie uses NVDA to move to a table, navigate its content, and find specific information. The appropriate HTML elements (or ARIA roles) inform her about the characteristics of the table and give her access to keyboard commands specifically for navigating the table’s content.

An interesting takeaway: Keyboard focus and screen reader focus are not the same thing. Contrary to what you might have heard, you do not need to make each cell of a table focusable with a keyboard to aid navigation. If the content inside the cell is non-interactive, you’ll likely make keyboard users work much harder to navigate the table than you intended. You can also watch a Smashing TV video with Léonie on How A Screen Reader User Accesses The Web (73 mins).

Website Features That Annoy Screen Reader Users

A missing alt caption, an auto-playing video, unlabelled buttons, poor use of headings, inaccessible web forms — what might seem like a small issue for sighted users can make the difference between being able to use a website independently or not for blind and visually impaired people. Holly Tuke knows this from her own experience.

To raise awareness for common accessibility issues, Holly summarized five annoying website features she faces as a screen reader user every single day, and, of course, how to fix them. Chris Ashton also published a piece explaining common issues that screen reader users have, which are often neglected in conversations focus on semantics and keyboard-accessibility alone. Little details that make a huge difference (thanks to Alex Chudesnov for the tip!).

Accessible Video/Audio Players

It’s not uncommon to see viewers frequently using captions when watching a short clip or a lengthy movie these days. We might be consuming the video in a noisy environment, or perhaps we can better understand written language, or perhaps we are currently busy with something else and need to look up something quickly without having to resort to headphones. Beyond that, how often do we use keyboard’s <space> to prompt a pause, or key arrows to move back and forward? Still, many video players and custom solutions don’t provide this functionality out of the box.

Accessible HTML5 Media Players provides an overview of accessible audio and video players. There are plenty of great open-source options, e.g. AblePlayer seems to be one of the reliable ones. It includes a full set of player controls that are keyboard-accessible, properly labelled for screen reader users, and controllable by speech recognition users, features high contrast, supports closed captions and subtitles, chapters, text-based audio description, an interactive transcript feature and automatic text highlighting. It supports YouTube and Vimeo videos. Hower, it depends on jQuery.

Alternatively, you could look into Vime.js as well: fully open-source, lightweight, fully customizable and without third-party dependencies. Other great options like Plyr and Accessible HTML5 Video Player by PayPal are similar. The latter is fully accessible to keyboard-only users and screen reader users, written in vanilla JavaScript, is additionally provided as a React component, and falls back to browser’s native controls if JavaScript is unavailable (thanks for the tip, @jamsandwich!).

Accessible Cookie Consent Prompts

Overlays and pop-ups are always problematic. But especially for screen reader users, sometimes those prompts are incredibly difficult to deal with to set any settings or even confirm the usage of cookies on the site. In her 15-mins talk on "Screen readers and cookie consents", Leonie Watson goes into detail explaining the poor experiences that compliance pop-ups have for accessibility. In some cases, users glide past consent prompts without being aware of them, in the others, the prompt are impossible to accept, resulting in an inability to use the site at all.

So how can we make them better? In Cookie banners and accessibility, Sheri Byrne-Haber highlights common issues that cookie prompts usually have: from how they visually appear to focus traps, the appearance in the tab order, the type of acceptance and alternate formats of consent disclosure. Quentin Bellanger provides a basic code example of a cookie consent modal and a tutorial along with it. There are also free open-source solutions: Osano Cookie Consent and cookie-consent-box, but they might require some accessibility work.

Accessible Close Buttons

“Close” buttons are everywhere — in modals, ads, confirmation messages, cookie prompts and any overlays that will appear in your interface. Unfortunately, the functionality is often limited to mouse users, leaving screen reader users and keyboard-users out. We can fix it.

In “Accessible Close Buttons” Manuel Matuzovic goes into deep details highlighting 11 examples and patterns of inaccessible close buttons as well as 5 examples of close buttons that work fairly well. The easiest way to solve the problem is to provide a button with visible text and only visually accessible icon and ensure that the description by screen readers isn’t polluted by the icon’s description. Manuel also provides code examples of 5 close buttons that you can apply to your work right away.

Accessible Inputs

In 2019, WebAIM analyzed the accessibility of the top one million websites, with a shocking conclusion: the percentage of error-free pages was estimated to be under one percent. To make our sites inclusive and usable for people who rely on assistive technology, we need to get the basics of semantic HTML right. With its credo of starting small, sharing, and working together, Oscar Braunert’s article on inclusive inputs is a great starting point to do so.

Starting off with the basics of WAI, ARIA, and WCAG, the article explores how to make inputs more accessible. The tips can be implemented without changing the user interface, and, as Oscar puts it: “If in doubt, just do it. Nobody will notice. Except some of your users. And they will thank you for it.”

Support User Preferences With prefers-reduced-*

Not every user is the same, and while some users love animations, others may have medical issues concerning motion. The prefers-reduced-motion media query lets you toggle animations on and off, but there are even more solutions to manage animations depending on a user’s preference. In his blog post, Elijah Manor addresses different techniques such as @media, matchMedia, and a custom React hook to address CSS, SVG SMIL, and JavaScript animations.

When it comes to making your content accessible to everyone, there’s another prefers-reduced-* media query that is worth knowing about — even though it isn’t supported by browsers yet (but you can emulate it in Polypane and Chromium browsers): prefers-reduced-data. It indicates when a user wants to use as little data as possible — if their connection is slow, for example, or if data is limited.

A Complete Guide To Dark Mode On The Web

Dark mode is quickly becoming a user preference with Apple, Windows, and Google having it implemented into their operating systems. But what about dark mode on the web? Adhuham wrote a comprehensive guide to dark mode that delves into different options and approaches to implementing a dark mode design on the web.

To start off, the guide looks at the technical considerations that implementing a dark mode entails, covering different approaches to toggling the themes and how to store a user’s preferences so that they will be applied consistently throughout the site and on subsequent visits. Tips for handling user agent styles with the color-scheme meta tag help avoid potential FOIT situations.

Design considerations are also tackled, of course, with valuable tips to get images, shadows, typography, icons, and colors ready for dark mode. While on it: to ensure we don’t unintentionally break the high contrast in mode, take a look at Styling for Windows High Contrast mode (thanks for the tip, Courtney Heitman!).

Accessible App-Wide Keyboard Navigation

A well-thought-out concept for keyboard navigation benefits everyone: It enables people who can’t comfortably use a mouse, assists screen reader users in interacting with an application, and it provides power users with more shortcuts to work as efficiently as possible. Usually, keyboard support is limited to specific shortcuts, but the team at Discord decided to go a step further with their application and expand keyboard support to, well, everything.

The case study “How Discord Implemented App-Wide Keyboard Navigation” shares valuable insights into how they tackled the task — and the challenges they faced along the way, of course. One turned out to be particularly difficult: How to consistently indicate where focus is on the page? As existing solutions for Focus Rings didn’t work out, the team had to build their own solution from scratch and made the code open source. If you’re facing a similar challenge, this one’s for you.

Accessible Comics

When we use slightly more complex shapes and layouts on the web, sometimes it appears to be so much easier to just save it as a foreground or background image and serve different images to small and large screens. This holds true for complicated charts and graphs as well as good old comics with speaking bubbles, but what if we could re-imagine the experience altogether?

Comica11y is an experiment by Paul Spencer that aims to achieve an all-inclusive online comic reading experience. What if we could have different reading modes for the comic, e.g. with closed captions, proper focus management to navigate between panels, high-contrast mode, SVG color blindness filters, programatic bubbles, selectable and translatable text, LTR and RTL support, and even adjustable font sizes? A wonderful initiative that shows just how far we can take UI challenges and use the web to enhance the experience greatly.

Inaccessible Disabled Buttons

It has become quite common for lengthy web forms to keep the “Continue” button disabled until the customer has provided all data correctly. This behavior acts as an indicator that something is wrong with the form, and it can’t be completed without reviewing the input. This works if the inline validation for every input field is working well, and it doesn’t work at all when it’s glitchy or buggy.

In “Disabled Buttons Suck”, Hampus Sethfors highlights the downsides of disabled buttons. With them in place, we do communicate that something is wrong, but we don’t really explain what is wrong, or how to fix it. So if the customer has overlooked an error message — be it in a lengthy form on desktop, or even in a short form on mobile, they’ll be lost. In many ways, keeping buttons active and communicating errors is more efficient.

And if it’s not possible, at least provide a way out with a button “I can’t complete the form, please help”, so customer support can get back to customers in case they get into trouble. If you need a more detailed refresher on web forms, "Form Design From One to Zero” will keep you busy.

But First, Accessibility Support

There are many different ways that assistive technologies interact with browsers and code. Since it’s still not possible to fully automate screen readers and voice control softwares, we are left with having to do manual tests. And that’s where a11ysupport.io comes into play.

Originally created by Michael Fairchild, this community-driven website aims to help inform developers about what is accessibility supported. It’s a project that is active and contributions are always welcome, so start testing away. Also, it’s always worth checking the WAI-ARIA authoring practices which describe essential semantics, roles, and ARIA necessary for common components and patterns (thanks to Stephanie Eckles for the tip!).

Accessibility Resources And Checklists

Accessibility is incredibly important, but, unfortunately, often overlooked. The community-driven A11Y Project attempts to make digital accessibility easier, providing designers and developers with the know-how they need to build beautiful, accessible, and inclusive experiences.

From the basic principles behind accessible design to conducting an accessibility audit, and cultivating community, The A11Y Project takes a 360 degree look at the topic. You’ll find articles just like quick tips, tips on books to read, newsletters to follow, as well as handy tools, groups committed to accessibility, and much more.

Repository Of Accessibility Tools

Do you ever get that itching feeling of forgetting something before shipping a project? Well, checklists are known to be the key to keeping an overview of things that need to be done and taken care of before the final showdown. When it comes to accessibility, there’s a growing list of tools and resources that are bound to help you keep an eye on things: A11y Resources.

Curated by Hannah Milan, this list was initially created to keep track of more than 200+ hand-curated accessibility plugins, tools, articles, case studies, design patterns, design resources, accessibility standards, and even checklists. Of course, you can always submit a tool if you see anything missing.

Wrapping Up

There are definitely dozens and hundreds of important guidelines by incredible people in the accessibility community, such as Steve Faulkner with a huge series of articles on semantics and accessibility and Leonie Watson with a huge series of articles on accessibility in general. It’s impossible to list everyone, but we are sincerely grateful to every contribution.

We probably have missed some important and valuable techniques and resources! So please leave a comment and refer to them — we’d love to update this post and keep it up-to-date for us all to be able to get back to it and build reliable, accessible components faster.

We sincerely hope that these tools and techniques will prove to be useful in your day-to-day work — and most importantly help you avoid some time-consuming, routine tasks.

Stay accessible!

Thank you! ❤️

A huge thanks to @jamsandwich, Courtney Heitman, Stephanie Eckles, Adam Silver, Daniela Kubesch, Tanisha Sabherwal, Manuel Matuzović, Vadim Makeev, Kitty Giraudel, Ian James, Juha Lehtonen, Heydon Pickering, Shivani Gupta, Jason Webb, Alex Kallinikos, Scott O'Hara, Sara Soueidan, Sasha Chudesnov, Adam Liptrot, Holger Bartel, Kim Johannesen and everybody else who has been passionately working all around accessibility for the contributions to this article. Community matters.

More On Accessibility

Accessible Web Animation: The WCAG on Animation Explained

It’s true, web animation can be accessible! Sometimes it just takes a little extra effort to make sure that it is. There are strategic things we can do to make sure our animations have a positive impact on accessibility, like planning how they contribute to the overall UX and ease of use of our site. There are also more tactical considerations for making sure the animations on our site are accessible, and that’s where the Web Content Accessibility Guidelines (WCAG) comes in.

While different contexts can affect the details of what you need to do, the WCAG provides a number of recommendations for animated content and interactions. These include guidelines for when to provide pause and play controls, limits for blinking or flashing the screen, and advice on when to provide reduced motion options for users with motion sensitivities. If you haven’t looked at it in a while, the specification has been updated to version 2.1, and now has even more useful guidance on how we can design web animations that are accessible. 

Let’s dig into each of those recommendations in more detail to see how we can apply them to our work on the web:

Pause, Stop, Hide 

The first of the WCAG recommendations that applies specifically to animation is Pause, Stop, Hide. For this one, the title gives a pretty big clue into what the recommendation is all about. It states:

For any moving, blinking or scrolling information that (1) starts automatically, (2) lasts more than five seconds, and (3) is presented in parallel with other content, there is a mechanism for the user to pause, stop, or hide it unless the movement, blinking, or scrolling is part of an activity where it is essential; […]

The recommendation specifically applies to motion initiated by the web page without user interaction, and it might sound like something that doesn’t apply to UI animation work at first. Most of the durations we might use in UI animation work are far under this five second threshold individually. But there are some common patterns where this would apply. For example: auto-advancing carousels or slideshows, animated backgrounds, or animated illustrations. While each individual animation within these patterns might still be very short, the overall motion that is created often plays out over more than five seconds. This is especially true when these are designed to play on an infinite loop, which is most definitely longer than five seconds.

How to meet the Pause, Stop, Hide criteria

If you have some of these longer playing animations, you’ll need to add some kind of pause and play controls that allow users to control the motion and/or auto playing behaviour. The WCAG specification doesn’t dictate what these controls need to look like though, you have complete design control over that.

A good example of this in practice is how the article series “Dark Side of The Grid” handles the example animations. Each animated figure loops infinitely once it starts, so they provide a play/stop button for readers to play the animation when they want to see it, and stop it when they’re done. Other more decorative or illustrative animations in the article play once and then present a button to replay them, if users want to. The placement and design of the buttons also fits the aesthetic of the overall design of the article which makes them both functional and aesthetically pleasing. 

Animated GIFs are something to look out for too. If you’ve got a looping animated GIF, that’s going to need some sort of pause/play controls to successfully meet this criteria. Both of the techniques mentioned in this post are helpful for pulling that off. 

There are some exceptions for this recommendation, as noted by the WCAG. One exception specifically worth noting is loaders and preloaders.

Three flashes or below threshold 

This recommendation is one that probably has the most research behind it because it stems from the days of broadcast TV. The main reason behind this recommendation is that significant flashing on screen has been known to trigger seizures.

In short three flashes or below threshold states: 

Web pages do not contain anything that flashes more than three times in any one second period, or the flash is below the general flash and red flash thresholds.

How to meet the three flashes or below threshold criteria

The WCAG provides details on the size, ratio and viewing angle thresholds under which flashing the screen could be considered safe. But for most of us, it’s probably easiest to avoid anything that flashes more than three times in one second. I don’t think many UX designers set out to flash the screen excessively on purpose, but it can happen. For example, a design that’s going for a video game sort of feel or a glitchy vibe might involve some screen flashing that happens more frequently than three times in a second. 

One specific example of a design that includes a significant amount of flashing is this article from the Huffington Post, pictured below. It’s a highly stylized piece on how millennials have a tougher go at things like jobs and saving for retirement than previous generations. Its  glitchy 8-bit video game design is very on point with the theme of the article. Design-wise, it’s a great choice for the subject matter and is well executed. But there are times, as you can see from the frame-by-frame stills below, where the text color flashes more than three times a second. 

This amount of flashing could be problematic for people with epilepsy or other physical reactions triggered by flashing. To their credit, the Huffington Post also provided a text-only version of the article for anyone sensitive to flashing, as Eileen mentions in this post, as well as advanced warning of the potential flashing hazard.

In general, avoiding effects that require frequent flashing is the safest way to meet this criteria. However, If you can’t avoid flashing animations in your project the WCAG provides detailed instructions around the safe thresholds for flashing the screen. Also, providing advanced warning of flashing content and an alternate version of the content without the flashing effect (like the example above) is a good thing to do as well.  

The A, AA, and AAA levels of the WCAG 

The WCAG has multiple levels of criteria and conformance, which is why each recommendation has a notation of what level it falls under. Level A compliance is the minimum level of conformance. Level AA is the middle level of conformance and indicates that the criteria for both level A and AA have been met. Level AAA is the highest level of conformance and requires satisfying the criteria from level A, AA and AAA. Typically, the guidelines found in level AAA require additional effort to meet. (If you want to learn more about these levels and what’s included in them outside of the animation-related recommendations we’re covering here, I’ve put together a list of helpful resources at the end of this article.) 

In general, most people are aiming for level AA compliance when they say they are making an accessible website. This is also the level you might see requested in an RFP or project brief. The last two guidelines we discussed fall under the level AA criteria and, therefore, must be met to claim level AA compliance. The next guideline, however, is part of the level AAA criteria. Even though it’s outside of the typical level of conformance, it’s a very useful recommendation to take into consideration if your project relies significantly on animation. I highly recommend implementing it in your work.

Animation from interactions 

This guideline covers a different kind of animation than the previous two. While the first two are generally applied to animation that’s initiated by the web page itself, this one applies to animation initiated by user interaction. More specifically, it states: 

Motion animation triggered by interaction can be disabled, unless the animation is essential to the functionality or the information being conveyed.

At first read, the term “motion animation” can be confusing since we typically use the terms “motion” and “animation” interchangeably. It might seem overly specific at first, but it makes sense to get this specific in this case. The WCAG defines motion animation as animation that is used to ”create the illusion of movement”, and specifies that “motion animation does not include changes of color, blurring or opacity.”

Essentially, the term motion animation is used to indicate that certain types of animation create the sense of movement, while others do not. It’s those animations that create a sense of movement that concerns this guideline. It’s important to keep that distinction in mind when discussing animation and accessibility to help make sure you focus your efforts efficiently. If we were to express this distinction in a very eyeball-like Venn Diagram, it would look  like this:

A large light purple circle with the word Animation on it in white with a smaller white circle contained at the bottom of the larger circle with the word Motion on it in black.

Over the last few years, we’ve come to realize that some types of motion on screen, even as part of an interface, can cause people with motion sensitivities to become physically ill. So this is why we might want to consider creating a reduced motion mode in our  work. I wrote more about the kinds of motion effects that are most likely to be triggering in this article, and this post from the WebKit blog covers some examples in detail.

How to meet the animation from interactions criteria

The WCAG suggests we avoid unnecessary animation, provide a control for users to turn off any non-essential motion, or take advantage of the reduced motion setting in operating systems and user agents. Let’s look at each of these in a bit more detail. There are a few different things we can do to help avoid exposing people to animation that might make them dizzy, nauseous, or worse.

Avoid unnecessary animation

Context and expectations also play a role here. The amount of motion you might reasonably expect to encounter on a website for a movie or video game is very different from what you might reasonably expect to encounter on say a government site or construction company’s site. The same amazing effects that might fit in just fine on a video game’s site would feel unnecessary or out of place on, say, a government website. Consider the context and expectations that apply to your site and whether the amount of animation you’re using in your design fits that context. 

Provide a way for users to turn off potentially problematic motion animation

If you have motion in your product that might be a trigger for folks with motion sensitivities, providing a way for users to avoid those triggering animations is the responsible thing to do. Based on the WCAG’s definition, any effect that could be considered motion animation should be one that includes a reduced version. 

Parallax effects are a good example. Those are universally problematic for folks with motion sensitivities based on my own research, yet it’s also still a very popular technique. While it wouldn’t be realistic to call for an end to all parallax effects entirely, implementing parallax responsibly requires giving your users some level of control to turn off that triggering motion. 

Typically, this is interpreted as including a toggle, setting, or preference for users to indicate their preference for reduced motion, and providing reduced versions of those motion animation effects when it’s activated. The Netlify 1 Million Devs site is one example of a motion toggle in action, and the official Animal Crossing site has one too.

Showing a screenshot of Netlify's Thanks a Million webpage. A toggle to disable animation is located in the top left corner of the page, above the content, which is set against a mint green background.
Take advantage of the reduce motion feature 

Sites or apps that don’t rely heavily on large amounts of motion might find that a custom toggle isn’t the right strategy for them, and instead use the prefers-reduced-motion media query on its own. This allows you to provide a reduced version of highly animated content when that preference is present globally via the user’s operating system. It’s also a setting they can set in one place and have it affect a variety of content they encounter. That makes it a great tool for us to use to detect and respond to a user’s need for reduced motion. 

I’ve written about using prefers-reduced-motion in detail over at Smashing Magazine, and it’s also been covered by other articles on this site. In short, it allows us to access someone’s OS-level motion preference via a media query. We can access it in CSS or JavaScript and use the returned value to provide a reduced motion experience for those who want it. For example, we could do this to create a reduced motion variation of a bouncing CSS animation:  

/* A constant bouncing motion effect applied to the title */
h2 {
  animation: bouncing 1.5s linear infinite alternate;
}

/* Replace it with a safer effect when prefers-reduced-motion returns true */
@media (prefers-reduced-motion: reduce) {
  h2 {
    animation: fade 0.5s ease-in both;
  }
}

Some sites opt to use both a custom toggle and reduced motion preferences together. If you go to the site with reduced motion requested in your operating system settings, you automatically get the reduced motion mode. This two-pronged approach is a great strategy for sites with large amounts of motion. Marcy Sutton covers the basics of how to set up this approach in her egghead.io course, as well as in this CodePen demo

Use these guidelines for your next animation project

There you have it, everything the WCAG says about animation explained in one place. I hope this article will help you to confidently make your web animation work accessible. Sometimes it takes a little extra effort, but that extra effort is totally worth it when it means you’ve expanded the number of people who can meaningfully interact with your site.

This article focused on the recommendations specific to animation, but animation isn’t the only place in our work where accessibility considerations can make a big impact. There are some great resources on accessibility out there that cover a more holistic view on accessibility. One of my favorites is the book Accessibility for Everyone by Lara Kalbag. Sites like WebAIM and the A11y Project are great ones to check out for a wealth of resources. If you’re doing a lot of your animation work with SVG, Heather’s SVG accessibility article is a good resource as well. I highly recommend checking out these resources if you haven’t already. 


The post Accessible Web Animation: The WCAG on Animation Explained appeared first on CSS-Tricks.

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

Programming Sass to Create Accessible Color Combinations

We’re all looking for low-hanging fruit to make our sites and apps more accessible. One of the easier things we can do is make sure the colors we use are easy on the eyes. High color contrast is something that benefits everyone. It not only reduces eye strain in general, but is crucial for folks who deal with reduced vision.

So let’s not only use better color combinations in our designs but find a way to make it easier for us to implement high contrasts. There’s one specific strategy we use over at Oomph that lets a Sass function do all the heavy lifting for us. I’ll walk you through how we put that together.

Want to jump right to the code because you already understand everything there is to know about color accessibility? Here you go.

What we mean by “accessible color combinations”

Color contrast is also one of those things we may think we have handled. But there’s more to high color contrasts than eyeballing a design. There are different levels of acceptable criteria that the WCAG has defined as being accessible. It’s actually humbling to crack open the WebAIM Contrast Checker and run a site’s color combinations through it.

My team adheres to WCAG’s Level AA guidelines by default. This means that: 

  • Text that is 24px and larger, or 19px and larger if bold, should have a Color Contrast Ratio (CCR) of 3.0:1.
  • Text that is smaller than 24px should have a CCR of 4.5:1.

If a site needs to adhere to the enhanced guidelines for Level AAA, the requirements are a little higher:

  • Text that is 24px and larger, or 19px and larger if bold, should have a CCR of 4.5:1.
  • Text that is smaller than 24px should have a CCR of 7:1.

Ratios? Huh? Yeah, there’s some math involved here. But the good news is that we don’t need to do it ourselves or even have the same thorough understanding about how they’re calculated the way Stacie Arellano recently shared (which is a must read if you’re into the science of color accessibility).

That’s where Sass comes in. We can leverage it to run difficult mathematical computations that would otherwise fly over many of our heads. But first, I think it’s worth dealing with accessible colors at the design level.

Accessible color palettes start with the designs

That’s correct. The core of the work of creating an accessible color palette starts with the designs. Ideally, any web design ought to consult a tool to verify that any color combinations in use pass the established guidelines — and then tweak the colors that don’t. When our design team does this, they use a tool that we developed internally. It works on a list of colors, testing them over a dark and a light color, as well as providing a way to test other combinations. 

ColorCube provides an overview of an entire color palette, showing how each color performs when paired with white, black, and even each other. It even displays results for WCAG Levels AA and AAA next to each result. The tool was designed to throw a lot of information at the user all at once when evaluating a list of colors.

This is the first thing our team does. I’d venture to guess that many brand colors aren’t chosen with accessibility at the forefront. I often find that those colors need to change when they get translated to a web design. Through education, conversation, and visual samples, we get the client to sign off on the new color palette. I’ll admit: that part can be harder than the actual work of implementing accessible colors combinations.

The Color Contrast Audit: A typical design delivery when working with an existing brand’s color palette. Here, we suggest to stop using the brand color Emerald with white, but use an “Alt” version that is slightly darker instead. 

The problem that I wanted to solve with automation are the edge cases. You can’t fault a designer for missing some instance where two colors combine in an unintended way — it just happens. And those edge cases will come up, whether it is during the build or even a year later when new colors are added to the system.

Developing for accessibility while keeping true to the intent of a color system

The trick when changing colors to meet accessibility requirements is not changing them so much that they don’t look like the same color anymore. A brand that loves its emerald green color is going to want to maintain the intent of that color — it’s “emerald-ness.” To make it pass for accessibility when it is used as text over a white background, we might have to darken the green and increase its saturation. But we still want the color to “read” the same as the original color.

To achieve this, we use the Hue Saturation Lightness (HSL) color model. HSL gives us the ability to keep the hue as it is but adjust the saturation (i.e. increase or decrease color) and lightness (i.e. add more black or more white). The hue is what makes a green that green, or a blue that blue. It is the “soul” of the color, to get a little mystical about it. 

Hue is represented as a color wheel with a value between 0° and 360° — yellow at 60°, green at 120°, cyan at 180°, etc. Saturation is a percentage ranging from 0% (no saturation) to 100% (full saturation). Lightness is also a value that goes from 0% to 100%, where no lightness is at 0%, no black and no white is at 50%, and 100% is all lightness, or very light.

A quick visual of what tweaking a color looks like in our tool:

With HSL, changing the low-contrast green to a higher contrast meant changing the saturation from 63 to 95 and the lightness from 45 to 26 on the left. That's when the color gets a green check mark in the middle when used with white. The new green still feels like it is in the same family, though, because the Hue remained at 136, which is like the color’s “soul.”

To learn more, play around with the fun HSL visualizer mothereffinghsl.com. But for a more in-depth description of color blindness, WCAG color contrast levels, and the HSL color space, we wrote an in-depth blog post about it.

The use case I want to solve

Designers can adjust colors with the tools that we just reviewed, but so far, no Sass that I have found could do it with mathematical magic. There had to be a way. 

These are some similar approaches I have seen in the wild:

  • An idea by Josh Bader uses CSS variables and colors split into their RGB values to calculate whether white or black is the best accessible color to use in a given situation.
  • Another idea by Facundo Corradini does something similar with HSL values and a very cool “switch function” in CSS.

I didn't like these approaches. I didn’t want to fallback to white or black. I wanted colors to be maintained but adjusted to be accessible. Additionally, changing colors to their RGB or HSL components and storing them with CSS variables seemed messy and unsustainable for a large codebase.

I wanted to use a preprocessor like Sass to do this: given two colors, automagically adjust one of them so the pair receives a passing WCAG grade. The rules state a few other things to consider as well — size of the text and whether or not the font is bold. The solution had to take this into account. 

In code terms, I wanted to do this:

// Transform this non-passing color pair:
.example {
  background-color: #444;
  color: #0094c2; // a 2.79 contrast ratio when AA requires 4.5
  font-size: 1.25rem;
  font-weight: normal;
}


// To this passing color pair:
.example {
  background-color: #444;
  color: #00c0fc; // a 4.61 contrast ratio
  font-size: 1.25rem;
  font-weight: normal;
}

A solution that does this would be able to catch and handle those edge cases we mentioned earlier. Maybe the designer accounted for a brand blue to be used over a light blue, but not a light gray. Maybe the red used in error messages needs to be tweaked for this one form that has a one-off background color. Maybe we want to implement a dark mode feature to the UI without having to retest all the colors again. These are the use cases I had in mind going into this. 

With formulas can come automation

The W3C has provided the community with formulas that help analyze two colors used together. The formula multiplies the RGB channels of both colors by magic numbers (a visual weight based on how humans perceive these color channels) and then divides them to come up with a ratio from 0.0 (no contrast) to 21.0 (all the contrast, only possible with white and black). While imperfect, this is the formula we use right now:

If L1 is the relative luminance of a first color 
And L2 is the relative luminance of a second color, then
- Color Contrast Ratio = (L1 + 0.05) / (L2 + 0.05)
Where
- L = 0.2126 * R + 0.7152 * G + 0.0722 * B
And
- if R sRGB <= 0.03928 then R = R sRGB /12.92 else R = ((R sRGB +0.055)/1.055) ^ 2.4
- if G sRGB <= 0.03928 then G = G sRGB /12.92 else G = ((G sRGB +0.055)/1.055) ^ 2.4
- if B sRGB <= 0.03928 then B = B sRGB /12.92 else B = ((B sRGB +0.055)/1.055) ^ 2.4
And
- R sRGB = R 8bit /255
- G sRGB = G 8bit /255
- B sRGB = B 8bit /255

While the formula looks complex, it’s just math right? Well, not so fast. There is a part at the end of a few lines where the value is multiplied by a decimal power — raised to the power of 2.4. Notice that? Turns out that it’s complex math which most programming languages can accomplish — think Javascript’s math.pow() function — but Sass is not powerful enough to do it. 

There’s got to be another way…

Of course there is. It just took some time to find it. 🙂

My first version used a complex series of math calculations that did the work of decimal powers within the limited confines of what Sass can accomplish. Lots of Googling found folks much smarter than me supplying the functions. Unfortunately, calculating only a handful of color contrast combinations increased Sass build times exponentially. So, that means Sass can do it, but that does not mean it should. In production, build times for a large codebase could increase to several minutes. That’s not acceptable. 

After more Googling, I came across a post from someone who was trying to do a similar thing. They also ran into the lack of exponent support in Sass. They wanted to explore “the possibility of using Newtonian approximation for the fractional parts of the exponent.” I totally understand the impulse (not). Instead, they decided to use a “lookup table.” It’s a genius solution. Rather than doing the math from scratch every time, a lookup table provides all the possible answers pre-calculated. The Sass function retrieves the answer from the list and it’s done.

In their words:

The only part [of the Sass that] involves exponentiation is the per-channel color space conversions done as part of the luminance calculation. [T]here are only 256 possible values for each channel. This means that we can easily create a lookup table.

Now we’re cooking. I had found a more performant direction. 

Usage example

Using the function should be easy and flexible. Given a set of two colors, adjust the first color so it passes the correct contrast value for the given WCAG level when used with the second color. Optional parameters will also take the font size or boldness into account.

// @function a11y-color(
//   $color-to-adjust,
//   $color-that-will-stay-the-same,
//   $wcag-level: 'AA',
//   $font-size: 16,
//   $bold: false
// );


// Sass sample usage declaring only what is required
.example {
  background-color: #444;
  color: a11y-color(#0094c2, #444); // a 2.79 contrast ratio when AA requires 4.5 for small text that is not bold
}


// Compiled CSS results:
.example {
  background-color: #444;
  color: #00c0fc; // which is a 4.61 contrast ratio
}

I used a function instead of a mixin because I preferred the output of a single value independent from a CSS rule. With a function, the author can determine which color should change. 

An example with more parameters in place looks like this:

// Sass
.example-2 {
  background-color: a11y-color(#0094c2, #f0f0f0, 'AAA', 1.25rem, true); // a 3.06 contrast ratio when AAA requires 4.5 for text 19px or larger that is also bold
  color: #f0f0f0;
  font-size: 1.25rem;
  font-weight: bold;
}


// Compiled CSS results:
.example-2 {
  background-color: #087597; // a 4.6 contrast ratio
  color: #f0f0f0;
  font-size: 1.25rem;
  font-weight: bold;
}

A deeper dive into the heart of the Sass function

To explain the approach, let’s walk through what the final function is doing, line by line. There are lots of helper functions along the way, but the comments and logic in the core function explain the approach:

// Expected:
// $fg as a color that will change
// $bg as a color that will be static and not change
// Optional:
// $level, default 'AA'. 'AAA' also accepted
// $size, default 16. PX expected, EM and REM allowed
// $bold, boolean, default false. Whether or not the font is currently bold
//
@function a11y-color($fg, $bg, $level: 'AA', $size: 16, $bold: false) {
  // Helper: make sure the font size value is acceptable
  $font-size: validate-font-size($size);
  // Helper: With the level, font size, and bold boolean, return the proper target ratio. 3.0, 4.5, or 7.0 results expected
  $ratio: get-ratio($level, $font-size, $bold);
  // Calculate the first contrast ratio of the given pair
  $original-contrast: color-contrast($fg, $bg);
  
  @if $original-contrast >= $ratio {
    // If we pass the ratio already, return the original color
    @return $fg;
  } @else {
    // Doesn't pass. Time to get to work
    // Should the color be lightened or darkened?
    // Helper: Single color input, 'light' or 'dark' as output
    $fg-lod: light-or-dark($fg);
    $bg-lod: light-or-dark($bg);


    // Set a "step" value to lighten or darken a color
    // Note: Higher percentage steps means faster compile time, but we might overstep the required threshold too far with something higher than 5%
    $step: 2%;
    
    // Run through some cases where we want to darken, or use a negative step value
    @if $fg-lod == 'light' and $bg-lod == 'light' {
      // Both are light colors, darken the fg (make the step value negative)
      $step: - $step;
    } @else if $fg-lod == 'dark' and $bg-lod == 'light' {
      // bg is light, fg is dark but does not pass, darken more
      $step: - $step;
    }
    // Keeping the rest of the logic here, but our default values do not change, so this logic is not needed
    //@else if $fg-lod == 'light' and $bg-lod == 'dark' {
    //  // bg is dark, fg is light but does not pass, lighten further
    //  $step: $step;
    //} @else if $fg-lod == 'dark' and $bg-lod == 'dark' {
    //  // Both are dark, so lighten the fg
    //  $step: $step;
    //}
    
    // The magic happens here
    // Loop through with a @while statement until the color combination passes our required ratio. Scale the color by our step value until the expression is false
    // This might loop 100 times or more depending on the colors
    @while color-contrast($fg, $bg) < $ratio {
      // Moving the lightness is most effective, but also moving the saturation by a little bit is nice and helps maintain the "power" of the color
      $fg: scale-color($fg, $lightness: $step, $saturation: $step/2);
    }
    @return $fg;
  }
}

The final Sass file

Here’s the entire set of functions! Open this in CodePen to edit the color variables at the top of the file and see the adjustments that the Sass makes:

All helper functions are there as well as the 256-line lookup table. Lots of comments should help folks understand what is going on. 

When an edge case has been encountered, a version in SassMeister with debug output was helpful while I was developing it to see what might be happening. (I changed the main function to a mixin so I can debug the output.) Feel free to poke around at this as well.

Play with this gist on SassMeister.

And finally, the functions have been stripped out of CodePen and put into a GitHub repo. Drop issues into the queue if you run into problems. 

Cool code! But can I use this in production?

Maybe. 

I’d like to say yes, but I’ve been iterating on this thorny problem for a while now. I feel confident in this code but would love more input. Use it on a small project and kick the tires. Let me know how the build time performs. Let me know if you come across edge cases where passing color values are not being supplied. Submit issues to the GutHub repo. Suggest improvements based on other code you’ve seen in the wild. 

I’d love to say that I have Automated All the A11y Things, but I also know it needs to be road-tested before it can be called Production Ready™. I’m excited to introduce it to the world. Thanks for reading and I hope to hear how you are using it real soon.

The post Programming Sass to Create Accessible Color Combinations appeared first on CSS-Tricks.

New High Contrast Syntax Highlighting Themes

Since the beginning on CodePen, our default syntax highlighting theme on CodePen has been our take on "Twilight". I'm not even sure what the original canonical source of Twilight is, but just about everywhere that offers syntax highlighting themes has some version of it.

In addition to the default Twilight, we have a total of 6 dark and 6 light themes to choose from in your settings.

Now we have one more one each side!

These are entirely new and hand-crafted by our own Klare Frank. They are somewhat based on the colors from CodePen's own branding and design system colors.

High contrast dark
High contrast light

AAA

All the colors have been designed to pass WCAG AAA color contrast guidelines.

But I don't really need a high contrast theme as my vision is pretty good...

That's cool, me too. Sometimes I even worry that picking a high-contrast theme will be too intense for me and I'd prefer something more subtle. But that's not the case here at all, I've switched to using the dark theme myself and I find it entirely pleasing.

I'd highly recommend trying them out, as you may find out, as I have, that they are extremely nice to work in!

Default?

We haven't flipped over to one of them as the default theme for new users, but we just might! We'd love to tweak them if you see any bugs or have suggestions.

The post New High Contrast Syntax Highlighting Themes appeared first on CodePen Blog.