4

I'd like to enable high contrast mode on my site and put all accessibility-related rules inside the high contrast media query:

@media screen and (-ms-high-contrast: active) {
  /* All high contrast styling rules */
}

However, how do I enable such mode in JavaScript programmatically? I'd like to have a button for my users to toggle this feature on their will. Is it possible in any way? Am I doing it right? Maybe a better solution exists.

Thank you for your assistance.

Super Jade
  • 5,609
  • 7
  • 39
  • 61
Mike Doe
  • 16,349
  • 11
  • 65
  • 88
  • enable them in edge or other browsers? – Mechanic Apr 17 '20 at 18:16
  • There is no way to enable high contrast mode programmatically via JavaScript without using a browser plug in. It would have to be a Windows-only, Internet Explorer-only or pre-Chromium Edge-only solution. – Heretic Monkey Apr 17 '20 at 18:18
  • hey @emix could you clarify, do you want to allow users to toggle your High Contrast styles on and off in the browsers where you cannot determine if they have it switched on (but still let CSS media queries switch them on in IE)? Gave an answer but got downvoted so I may have got the wrong end of the stick! – GrahamTheDev Apr 19 '20 at 07:15

4 Answers4

2

Since media queries are automatic (based on browser conditions) you need to emulate that block in your CSS but apply and remove it based on a user interaction.

It seems to me that you should simply write the styles to emulate IE's high contrast mode and then toggle a class on the document (probably body) when you click a button.

Put your new class and sub definitions at the bottom of your CSS so you know they'll override previous properties.

For example:

h2 {
  font-size: 14px;
  color: #dddddd;
}
/* overrides a normal h2 when highContrast class is added to the body */
/* use a post processor (LESS/SCSS) to easily nest elements */
body.highContrast h2 {
  font-size: 18px;
  color: #000;
  font-weight: bold;
}
Bryce Howitson
  • 7,339
  • 18
  • 40
  • I already did so. Come to think about it, I could run the media query in JS and turn my mechanism on if it passes. – Mike Doe Apr 17 '20 at 20:34
  • 1
    Right just remember that media queries in CSS don't DO anything. It's just a condition that adds or removes a block of CSS. So you would have to figure out how to check those conditions in javascript. The accessibility conditions aren't consistent across browsers and OSes so be prepared for a lot of testing. – Bryce Howitson Apr 17 '20 at 20:44
0

Here’s the technique, which is really rather simple: create a media query using -ms-high-contrast, in which you place your IE 10 and 11-specific CSS styles. Because -ms-high-contrast is Microsoft-specific (and only available in IE 10+), it will only be parsed in Internet Explorer 10 and greater.

-ms-high-contrast supports two values: none and active. So to target IE10+ regardless of the property’s setting, use this media query:

@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { /* IE10+ CSS styles go here */

define the classes you want to add for ms-high-contrast mode.

Once the user clicks the button you have to add the relevant classes to the dom elements dynamically.

Note: This will only work in IE10+ browsers.

Amit
  • 193
  • 1
  • 8
  • 1
    This does not answer the question. The OP is asking how to enable High Contrast from a browser. This answer says how to use the `-ms-high-contrast` media query to limit styles to IE. – Heretic Monkey Apr 17 '20 at 18:15
  • Exactly. I’d like to enable such behavior in a cross browser manner. – Mike Doe Apr 17 '20 at 18:24
  • I agree to what @HereticMonkey posted what I wanted to highlight is we need to find someway to pass on the message from the browser to enable this mode to be activated. – Amit Apr 17 '20 at 18:25
  • FYI, the `-ms-high-contrast` media query also works for the [Edge Legacy](https://learn.microsoft.com/en-us/deployedge/microsoft-edge-sysupdate-access-old-edge) browser. – Super Jade Apr 19 '20 at 21:37
0

As @GrahamRitchie pointed out, while you can’t enable the browser’s high contrast settings via JavaScript, you can usually detect if it’s already enabled.

For most browsers on Windows 10, you can detect whether high contrast is enabled by

  1. creating a an element with a background color,

  2. appending it to the DOM, and

  3. testing to see whether the background color is still there:

isUsingHighContrastMode = () => {
  const testDiv = document.createElement('div');
  testDiv.style.color = 'rgb(50, 50, 50)';
  document.body.appendChild(testDiv);
  const color = document.defaultView!.getComputedStyle(testDiv, null).color;
  document.body.removeChild(testDiv);
  return color !== 'rgb(50, 50, 50)' ? true : false;
}

Chrome has its own High Contrast extension, and usually you don't need to detect it. But if you do, check for the hc attribute on the html tag:

const htmlTag = document.getElementsByTagName(
    'html'
  )[0];
const isUsingChromeHighContrastExtension: boolean =
    htmlTag.getAttribute('hc') !== null;

For MacOS, you can detect if the user has Invert colors enabled like this:

isUsingMacInvertedColors = () => {
      const mediaQueryList = window.matchMedia('(inverted-colors: inverted)');
      return mediaQueryList.matches;
}

Then you can apply your styling rules!


Note: I previously tried like crazy to detect other MacOS high contrast settings. The answers led me to stop trying for awhile, though I hope for a better solution in the future.

Super Jade
  • 5,609
  • 7
  • 39
  • 61
  • @SuperJade how do you determine if it is white on black or black on white high contrast mode? – springathing Sep 17 '20 at 20:54
  • @springathing For Windows 10 High Contrast, alter the JavaScript above to determine if the background is white or black. One caveat to this approach is that the user could change the background color to whatever color they want. Post a question for details. ;-) – Super Jade Oct 04 '20 at 03:18
0

Please note if you want to enable high contrast mode from the browser, you can't. This answer is how to apply your High Contrast Styles via a button click while maintaining the media query that doesn't rely on JavaScript to work.

This solution allows you to keep the styles for IE as a media query but also allow them to be toggled manually. It does depend on your high contrast CSS to be located in a separate external file.

What we do is add the style sheet that contains your high contrast CSS rules as an external file.

We give this file a unique ID (#doNotChangeMe) and the relevant media query media="screen and (-ms-high-contrast: active)".

As the above file will only work for IE we are safe to leave it alone.

We then create a function that can also add and remove this style sheet on a button click.

I created a simple toggle function that will query to see if the style sheet exists (without the #doNotChangeMe id) using a CSS selector.

'link[href*="' + externalFileName + '"]:not(#doNotChangeMe)' (look for a link with the href we provided, as long as it doesn't have the relevant ID).

We then see if this CSS file exists in the DOM, if it doesn't we add it again (which will cause no harm assuming your high contrast CSS is the last style sheet in the DOM), otherwise we remove it.

I did try to make this cleaner by changing the media query programatically, however this seemed to have mixed results in browsers and the above seems to work consistently (CORS security policy kicks in if you try to change media.mediaText for example).

I have linked to the Bootstrap style sheet in the example just for ease of demonstration. You will have to inspect the DOM to see that the high contrast style sheet is not touched by this function (or enable high contrast mode in IE to see that the toggle does not affect anything).

Please note I have not added any indicators to the toggle button to show whether the mode is active, make sure you add relevant WAI-ARIA, button text etc.

//Please note that the below assumes you do not want to interfere with normal media query, if you want people who do have high contrast mode enabled you will need to modify this to remove the ignoreIdOrClass part and instead have a variable containing the state.
var ignoreIdOrClass = '#doNotChangeMe'; //this is the ID of the file that was already in the DOM we do not want to touch
var externalFileName = document.querySelector(ignoreIdOrClass).href; //we grab the URL of the file we want to replicate

function toggleHighContrast() {
  var linkNode = document.querySelector('link[href*="' + externalFileName + '"]:not(' + ignoreIdOrClass + ')'); //see if we have added this style sheet to the DOM ourselves, ignore the one with the ID we said to ignore
  if(!linkNode){ //our css file copy doesn't exist so create it and add it to the document HEAD
  var head = document.head;
  var link = document.createElement("link");

  link.type = "text/css";
  link.rel = "stylesheet";
  link.href = externalFileName;
  
  head.appendChild(link);
  }else{ //our css copy does exist so remove it
    
    linkNode.parentNode.removeChild(linkNode);
  }
}

document.getElementById("myBtn").addEventListener("click", toggleHighContrast);
<link id="doNotChangeMe" rel="stylesheet" media="screen and (-ms-high-contrast: active)" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" />
<div style="padding: 20px;">
<button id="myBtn" class="btn btn-primary btn-lg">Toggle High Contrast</button>
</div>
GrahamTheDev
  • 22,724
  • 2
  • 32
  • 64
  • No probs, not bothered by downvotes I just assume I misunderstood the question when I get them. Write custom high contrast CSS, don't just emulate IE, it will be better. I would also not rely solely on JavaScript to determine a media query if you want maximum accessibility. I can see you are happy with the answer provided but perhaps just reread what I put here as a better way of implementing this vs body class as it has a lot of benefits that may not be immediately obvious (conditionally downloaded CSS for performance, still works on IE without JavaScript, easier to maintain). – GrahamTheDev Apr 19 '20 at 11:26
  • Also dont forget that if you turn this into something you can turn on and off you will need to use either a checkbox (high contrast - checkbox) or a toggle button (which is harder as you would need to toggle `aria-pressed`) that is accessible. If you need help there just let me know. – GrahamTheDev Apr 19 '20 at 11:31
  • 1
    FYI, the `-ms-high-contrast` media query also works for the [Edge Legacy](https://learn.microsoft.com/en-us/deployedge/microsoft-edge-sysupdate-access-old-edge) browser. – Super Jade Apr 19 '20 at 21:38