DEV Community

Cover image for Adding a high-contrast theme toggle to a pastel website
Ingo Steinke
Ingo Steinke

Posted on

Adding a high-contrast theme toggle to a pastel website

How can we make a legacy website more accessible? A common problem is the low color contrast of pastel designs, making text hard or impossible to read for a user with visual impairments.

Screenshot: colors inspired by a real WordPress theme and a WAVE result showing 164 contrast errors

How to provide an accessible high contrast alternative to a pastel color scheme?

I used to experiment with different strategies, but they all had disadvantages. A fixed color palette that meets the minimum contrast requirements reduces the artistic design possibilities. It might still not be contrasting enough for some individuals, and it might even distract other users who expect a pastel scheme for some reason. We can add another color scheme, much like a "reader mode", a print stylesheet or an alternative dark mode vs. light mode. Then we can query the current high-contrast preference and switch the theme accordingly. Even when it works, automatic accessibility audits might fail, and "tricking" them by delaying the switch for several seconds before fading out high contrast into a more pastel version is no best practice for various reasons.

How is it possible to ensure a website's color theme offers a high-contrast alternative which complies to the WCAG 2 minimum contrast requirements while preferring a pastel, low-contrast theme unless the user wants or needs higher contrast?

I tried to define a fallback theme with a higher contrast and providing…

Automatically switching themes based on prefers-high-contrast system settings might not be enough. Our users might not know where to change the settings, they might have a browser or operating system that does not support it, or they may want change their preference temporarily based on ambient light settings. Applying one of two or more alternative themes automatically might result in an unhelpful outcome, much like an automated language selection that cannot be changed.

WCAG 2.1 - high contrast toggle button

Although it has been criticized as "poor usability", a button to toggle high-contrast color schemes is perfectly valid to conform to WCAG criteria even without querying prefers-high-contrast, and it can be a helpful option to revise an unhelpful automatic preference.

Short answer, yes, a high contrast toggle button is sufficient to pass WCAG but is not the greatest user experience. There are also some caveats in doing so as mentioned in "G174: Providing a control with a sufficient contrast ratio that allows users to switch to a presentation that

When we decide how to switch themes, all we have to do is code them. The effort depends on how well our existing CSS and HTML markup is suited to be themed. Modern content management system themes often rely on variables, while legacy code will introduce custom and inline styles that will be hard to override, even if we use the combined force of specificity and !important.

Here is a (not so) "minimal" reproducible example inspired by an actual Elementor site. It contains tricky markup and unintuitive custom property variable names, like this

<div class="elementor-element elementor-element-895b404 elementor-widget elementor-widget-text-editor" data-id="895b404" data-element_type="widget" data-widget_type="text-editor.default">
  <div class="elementor-widget-container">
    <style>.elementor-widget-text-editor.elementor-drop-cap-view-stacked .elementor-drop-cap{background-color:#69727d;color:#fff}</style><p>
Enter fullscreen mode Exit fullscreen mode
.elementor-kit-9 {
    --e-global-color-edb7346: #A76CAA;
    --e-global-color-9399b1e: #000000; /* black */
    --e-global-color-4a62416: #8FCBB0; /* pastel green */
    --e-global-color-a30acc7: #FFFFFF; /* white */
Enter fullscreen mode Exit fullscreen mode

As you can see, the global color suffixes, have absolutely nothing to do with hex colors, but they look similar enough to confuse my mind and affect readability, so I added some perceived color names in the code comments.

Toggle and override

To toggle themes, I decided to add another class name to the document body (or to an addition content wrapper in my codepen example) and replace the system color palette before adding element-specific styles only where needed.

/* high contrast theme */
.elementor-kit-9.has-high-contrast-theme,
body.has-high-contrast-theme,
body.elementor-kit-9.has-high-contrast-theme {
  --e-global-color-4a62416: #e2fff2 !important; /* light greenish white */
  --e-global-color-a30acc7: #000000 !important; /* 100% black */
  --e-global-color-ec37af6: #191970; /* dark midnight blue */
  --e-global-color-9b687dd: #421504; /* dark orange brown */
  --e-global-color-dbe950d: #fffbea; /* light yellow-ish white */
}
Enter fullscreen mode Exit fullscreen mode

The button must have a high color contrast from the start, but we can use JavaScript to toggle our themes. This can be done with some lines of vanilla JS that should run in every recent major browser and a lot of discontinued clients like Internet Explorer or older Safari browsers, using var variables, a for loop and the oldest and most compatible state engine: the global window object.

document.addEventListener("DOMContentLoaded", function(event) { 
  window.hasHighContrastTheme = window.hasHighContrastTheme || false;
  window.elementorThemeRoot = document.querySelector('.elementor-kit-9') || document.body;
  var contrastThemeToggleButtons = document.querySelectorAll('.toggle-contrast');
  for (var i=0; i<contrastThemeToggleButtons.length; i++ ) {
    contrastThemeToggleButtons[i].addEventListener('click', function(e){
      window.hasHighContrastTheme = !window.hasHighContrastTheme;
      if (window.hasHighContrastTheme) {
        window.elementorThemeRoot.classList.add('has-high-contrast-theme');
      } else {
        window.elementorThemeRoot.classList.remove('has-high-contrast-theme');
      }
    })
  }
});
Enter fullscreen mode Exit fullscreen mode

We can add some CSS to indicate the state of the toggle button. Here is a very simple style to demonstrate the syntax.

.toggle-contrast {
  border: solid 1px silver;
}
body.has-high-contrast-theme .toggle-contrast {
  border: solid 2px black;
}
Enter fullscreen mode Exit fullscreen mode

Proof of concept: a CodePen

Top comments (2)

Collapse
 
moopet profile image
Ben Sinclair

Perhaps a silly question, but wouldn't it be better to make the accessible theme the default rather than offer it as an alternative?

Hopefully in the future, everyone's experience across websites will be based on their needs and preferences rather than ours anyway, but in the meantime the best we can do is to make things usable by as many people as possible out the box.

Collapse
 
ingosteinke profile image
Ingo Steinke

That's exactly the dilemma I failed to solve for a long time, as many graphic designers insist on keeping established corporate identity design systems which often don't comply with recommended contrast ratios. If you start with a high-contrast theme by default that doesn't match the brand design, you will violate hundreds of marketing rules instead, so you can guess what's the priority. And if it's text, users could switch the site to reader mode or use a screen reader no matter how the text looks like in the default setting - and then there is the new(?) prefers-contrast which would solve everything in theory, but not many people seem to know how to activate the setting.