Khan Engineering

Khan Engineering

We're the engineers behind Khan Academy. We're building a free, world-class education for anyone, anywhere.

Subscribe

Latest posts

Making Websites Work with Windows High Contrast Mode

Diedra Rater on March 21

Kotlin for Python developers

Aasmund Eldhuset on Nov 29, 2018

Using static analysis in Python, JavaScript and more to make your system safer

Kevin Dangoor on Jul 26, 2018

Kotlin on the server at Khan Academy

Colin Fuller on Jun 28, 2018

The Original Serverless Architecture is Still Here

Kevin Dangoor on May 31, 2018

What do software architects at Khan Academy do?

Kevin Dangoor on May 14, 2018

New data pipeline management platform at Khan Academy

Ragini Gupta on Apr 30, 2018

Untangling our Python Code

Carter J. Bastian on Apr 16, 2018

Slicker: A Tool for Moving Things in Python

Ben Kraft on Apr 2, 2018

The Great Python Refactor of 2017 And Also 2018

Craig Silverstein on Mar 19, 2018

Working Remotely

Scott Grant on Oct 2, 2017

Tips for giving your first code reviews

Hannah Blumberg on Sep 18, 2017

Let's Reduce! A Gentle Introduction to Javascript's Reduce Method

Josh Comeau on Jul 10, 2017

Creating Query Components with Apollo

Brian Genisio on Jun 12, 2017

Migrating to a Mobile Monorepo for React Native

Jared Forsyth on May 29, 2017

Memcached-Backed Content Infrastructure

Ben Kraft on May 15, 2017

Profiling App Engine Memcached

Ben Kraft on May 1, 2017

App Engine Flex Language Shootout

Amos Latteier on Apr 17, 2017

What's New in OSS at Khan Academy

Brian Genisio on Apr 3, 2017

Automating App Store Screenshots

Bryan Clark on Mar 27, 2017

It's Okay to Break Things: Reflections on Khan Academy's Healthy Hackathon

Kimerie Green on Mar 6, 2017

Interning at Khan Academy: from student to intern

Shadaj Laddad on Dec 12, 2016

Prototyping with Framer

Nick Breen on Oct 3, 2016

Evolving our content infrastructure

William Chargin on Sep 19, 2016

Building a Really, Really Small Android App

Charlie Marsh on Aug 22, 2016

A Case for Time Tracking: Data Driven Time-Management

Oliver Northwood on Aug 8, 2016

Time Management at Khan Academy

Several Authors on Jul 25, 2016

Hackathons Can Be Healthy

Tom Yedwab on Jul 11, 2016

Ensuring transaction-safety in Google App Engine

Craig Silverstein on Jun 27, 2016

The User Write Lock: an Alternative to Transactions for Google App Engine

Craig Silverstein on Jun 20, 2016

Khan Academy's Engineering Principles

Ben Kamens on Jun 6, 2016

Minimizing the length of regular expressions, in practice

Craig Silverstein on May 23, 2016

Introducing SwiftTweaks

Bryan Clark on May 9, 2016

The Autonomous Dumbledore

Evy Kassirer on Apr 25, 2016

Engineering career development at Khan Academy

Ben Eater on Apr 11, 2016

Inline CSS at Khan Academy: Aphrodite

Jamie Wong on Mar 29, 2016

Starting Android at Khan Academy

Ben Komalo on Feb 29, 2016

Automating Highly Similar Translations

Kevin Barabash on Feb 15, 2016

The weekly snippet-server: open-sourced

Craig Silverstein on Feb 1, 2016

Stories from our latest intern class

2015 Interns on Dec 21, 2015

Kanbanning the LearnStorm Dev Process

Kevin Dangoor on Dec 7, 2015

Forgo JS packaging? Not so fast

Craig Silverstein on Nov 23, 2015

Switching to Slack

Benjamin Pollack on Nov 9, 2015

Receiving feedback as an intern at Khan Academy

David Wang on Oct 26, 2015

Schrödinger's deploys no more: how we update translations

Chelsea Voss on Oct 12, 2015

i18nize-templates: Internationalization After the Fact

Craig Silverstein on Sep 28, 2015

Making thumbnails fast

William Chargin on Sep 14, 2015

Copy-pasting more than just text

Sam Lau on Aug 31, 2015

No cheating allowed!!

Phillip Lemons on Aug 17, 2015

Fun with slope fields, css and react

Marcos Ojeda on Aug 5, 2015

Khan Academy: a new employee's primer

Riley Shaw on Jul 20, 2015

How wooden puzzles can destroy dev teams

John Sullivan on Jul 6, 2015

Babel in Khan Academy's i18n Toolchain

Kevin Barabash on Jun 22, 2015

tota11y - an accessibility visualization toolkit

Jordan Scales on Jun 8, 2015

Meta

Making Websites Work with Windows High Contrast Mode

by Diedra Rater on March 21

At Khan Academy, we've been working to make our web product more accessible to a variety of users, including those who experience blurry vision and/or severe color-blindness. Part of this work has involved updating our styling to be able to work well with Windows High Contrast Mode.

This project required a little bit of learning via trial-and-error, with a lot of help from our dear friends at Level Access, so I wanted to share some of the things we discovered to make it easier for other developers working with accessibility. In this post, you'll learn about how to optimize web applications for the benefit of other users with visual accessibility needs.

What is it and who uses it?

Windows High Contrast Mode is an accessibility settings feature in the Windows operating system. As the name suggests, it changes the visual appearance of everything displayed to have higher contrast. This includes applications that are native to the Windows platform as well as web apps.

Khan Academy's logged out homepage without Windows High Contrast Mode:

Screenshot of Khan Academy's logged out homepage without Windows High Contrast Mode. There are light gray borders, light gray backgrounds, a dark blue button, and text of different colors.

Khan Academy's logged out homepage with Windows High Contrast Mode:

Screenshot of Khan Academy's logged out homepage with Windows High Contrast Mode. All the borders are now black, the background colors are white (including buttons), and all of the text is either blue (if it's a link) or black (if it's not a link).

The high contrast version of the web may not be as pretty, but it is easier to see and understand.

There are several different versions of Windows High Contrast Mode that can be used. The examples in this post use High Contrast White.

Screenshot of the Windows High Contrast Mode settings options in Windows. There are four options: 'High Contrast /#1', 'High Contrast /#2', 'High Contrast Black', and 'High Contrast White'. 'High Contrast White' has been selected and the settings is turned on.

What does it do?

Background colors and images

Background colors and background images are removed and replaced with white.

Both of these styles would be overridden:

background-color: lawngreen; /* No color for you. */
background-image: url("cousin_fal.gif"); /* No image either. */

(You can see an example of this in #1 of the Comparison section below.)

The opacity of background color styles will remain the same, though. The color will just be changed to white.

In this code example, the background is set to 50% black. In Windows High Contrast Mode, it will instead be 50% white.

background-color: rgba(0, 0, 0, 0.5); /* White as the driven snow if the driven show were 50% opacity. */

Key takeaway

Only use background-color and background-image CSS for decorative styling. Windows High Contrast Mode considers them unnecessary to understand the page.

Border and outline colors

For border colors and outline colors, it actually goes the other way. No matter what color or opacity you set, they'll be set to 100% black in Windows High Contrast Mode.

outline: 2px dotted lightsalmon; /* Black as your heart. */
border: 2px solid lavenderblush; /* And my coffee. */

(You can see an example of this in #2 of the Comparison section below.)

Key takeaway

Borders and outlines should be functional, not ornamental, because Windows High Contrast Mode retains them. Also, they won't be distinguishable if used to separate black sections created by <img> and SVG elements.

<img> and SVG elements

<img> and SVG elements are immune and will remain intact.

<img src="cousin_fal.gif" alt="Cousin Fal's at it again"> <!-- Impervious to Windows High Contrast Mode. -->
<svg> ... </svg> <!-- An svg of me being too lazy to write an svg example. 1,000,000 defense against Windows High Contrast Mode. -->

(You can see an example of this in #3 of the Comparison section below.)

Key takeaway

Semantic <img> and SVG elements are for important, functional elements. Windows High Contrast Mode assumes they're required to interpret the page.

Font colors

Font colors work the same as border and outline colors but with one more detail — the text element's background will be set to white. This is because the <img> and SVG elements are unchanged. If you have text on top of an <img> or SVG element, the text could become impossible to see if the image were dark when the text becomes black. By forcing the text to be black and have a white background, Windows High Contrast Mode ensures that the user will be able to read the text regardless of what <img> or SVG you may have behind it.

In this example, the text will be black with a white background.

color: lemonchiffon;

(You can see an example of this in #4 of the Comparison section below.)

Key takeaway

You don't need to worry about text color; it can handle itself.

Comparison

Here's a section of the screenshots of the Khan Academy logged out homepage. Here we can see examples of almost all the ways Windows High Contrast Mode alters our custom styling:

  1. The background color of the dark blue button and the light gray horizontal list of domains below the image are now white.
  2. The borders on all three buttons are now black.
  3. The image of the sky remains intact because it's been added as an SVG element.
  4. All the text on the page has returned to its default browser color (black for non-links and blue for links), and the text on top of the SVG image has a white background.

Section of the Khan Academy logged out homepage shown in two versions side by side. On the left side is the default mode, and on the right side is the Windows High Contrast Mode. The section shown includes a button that reads 'Learners, start here'. In the default version, the button has a dark blue background, white text, and no visible outline. In the Windows High Contrast Mode version, the button has a white background, black text, and a black outline. Behind the button is an image of a sky, which is the same in both versions.

Improve web applications in Windows High Contrast Mode — practical examples from our work

Quick note..

I wanted to mention up-front that there is a -ms-high-contrast CSS media feature that allows you to detect if the page is being viewed in Windows High Contrast Mode. However, it is non-standard and unreliable; therefore, none of the examples shown here utilize this feature.

Background colors and images

Use HTML <img>s and SVG's instead of background-color and background-image CSS for important elements in the design. Windows High Contrast Mode removes these characteristics of the page for a reason, so I don't want to encourage you to override that override altogether. But sometimes there are elements on the page that are visually necessary for users to understand the user interface and interact with it accordingly.

Replacing CSS background-image with an <img> element

Khan Academy video pages enable people to discuss the video, including links to specific points in the video in their comments. We visually communicate that the button jumps to a specific time in the video with a little blue play image to the left of the timestamp. This image was originally added using CSS's background-image. Of course, it was being removed in Windows High Contrast Mode and a big empty space was left instead.

To fix this, we removed the background-image from the CSS and added the image back with a img element so it would appear in both default and Windows High Contrast Modes.

Before

Screenshot:

Screenshot of the discussion feature on a video page. There's a post that reads 'At', a blank space, and then '0:23, does the picture display bacteria?'

CSS:

.videoPlayButton {
    background: url('/images/play.png') no-repeat left;
    padding-left: 18px;
    background-size: 14px 14px;

    /* Non-relevant styling has been removed for simplicity. */
}

JSX:

// This is the same in both versions.
const timestamp = "3:14"; // Just for an example, to simplify this code.
const timestampAria = `${timestamp} Click to jump to ${timestamp} in video`;

return (
    <button className='videoPlayButton' aria-label={timestampAria}>
        {timestamp}
    </button>
);
After

Screenshot:

Screenshot of the discussion feature on a video page. There's a post that reads 'At', a little blue play icon, and then '0:23, does the picture display bacteria?'

CSS:

.videoPlayButton {
    /* Non-relevant styling has been removed for simplicity. */
}
.videoPlayIcon {
    height: 14px;
    width: 14px;
    margin-top: -4px;
    vertical-align: middle;
}

JSX:

// This is the same in both versions.
const timestamp = "3:14"; // Just for an example, to simplify this code.
const timestampAria = `${timestamp} Click to jump to ${timestamp} in video`;

const playIcon = `<img src="/images/play.png" className="youTubePlayIcon" />`;

return (
    <button className='videoPlayButton' aria-label={timestampAria}>
        {playIcon} {timestamp}
    </button>
);

Replacing CSS background-color with an SVG element

Above the discussions feature on video pages, the video itself has a large play button on top. The actual play icon is an SVG HTML element, but it's white, and the color for the area around it was added using background-color CSS. Of course, in Windows High Contrast Mode, the background-color CSS was removed and replaced with white, which left users with a white icon on a white background. Oops!

There are a couple of different ways to fix this, but, in this case, we chose to replace the element behind the icon with another SVG, which would retain its color. (Another option would be to allow the color of the SVG element to be overridden, which is illustrated in the next example.)

Before

Screenshot:

Screenshot of a video page. On top of the video, there's a big white circle with a black outline.

CSS:

.playButton {
    background-color: #1865f2;
    width: 121px;
    height: 121px;

    /* Non-relevant styling has been removed for simplicity. */
}

JSX:

// This is the same in both examples.
const playIcon = `M1.6,9.9C1.5,10,1.3,10,1.2,10C1.1,10,1,10,0.9,9.9C0.7,9.8,0.6,9.6,
    0.6,9.4V0.6c0-0.2,0.2-0.4,0.4-0.5C1.1,0,1.4,0,1.6,0.1l7.6,4.5c0.2,0.1,
    0.3,0.3,0.3,0.5c0,0.2-0.1,0.4-0.3,0.5L1.6,9.9z`;

const PLAY_BUTTON_DIAMETER = 44;

<button
    aria-label="Play video"
    className="playButton"
    onClick={this.handlePlayButtonClick}
>
    <svg
        height={PLAY_BUTTON_DIAMETER}
        width={PLAY_BUTTON_DIAMETER}
    >
        <path
            d={playIcon}
            fill={Color.white}
            transform="scale(4.375, 4.375) translate(9.75, 8.25)"
        />
    </svg>
</button>
After

Screenshot:

Screenshot of a video page. On top of the video, there's a big white circle with a black outline and a blue play icon in the middle of the white circle.

CSS:

.playButton {
    /* Non-relevant styling has been removed for simplicity. */
}

JSX:

// This is the same in both examples.
const playIcon = `M1.6,9.9C1.5,10,1.3,10,1.2,10C1.1,10,1,10,0.9,9.9C0.7,9.8,0.6,9.6,
    0.6,9.4V0.6c0-0.2,0.2-0.4,0.4-0.5C1.1,0,1.4,0,1.6,0.1l7.6,4.5c0.2,0.1,
    0.3,0.3,0.3,0.5c0,0.2-0.1,0.4-0.3,0.5L1.6,9.9z`;

const PLAY_BUTTON_DIAMETER = 121;
const PLAY_BUTTON_RADIUS = Math.floor(PLAY_BUTTON_DIAMETER / 2);

<button
    aria-label="Play video"
    className="playButton"
    onClick={this.handlePlayButtonClick}
>
    <svg
        height={PLAY_BUTTON_DIAMETER}
        width={PLAY_BUTTON_DIAMETER}
    >
        <circle
            cx={PLAY_BUTTON_RADIUS}
            cy={PLAY_BUTTON_RADIUS}
            r={PLAY_BUTTON_RADIUS}
            stroke="#fff"
            strokeWidth="1"
            fill="#1865f2"
        />
        <path d={playIcon} fill="#fff" />
    </svg>
</button>

Add CSS background-color via a pseudo-element

You can also force the background-color CSS to remain by adding it to a pseudo-element. This isn't a tactic that we happened to use, but it is available if necessary.

CSS:

.element:after {
  content:"";
  background-color: someothercsscolornameyoudidntknowexisted;
}

Allowing the SVG color to be overridden using fillColor

The colors of SVG's will remain intact in Windows High Contrast Mode, but only if the colors are set directly on fill. If the svg image is white in your design, you should avoid setting that white color using fill: yourColor. Instead set fill to currentColor and set the color on a wrapper element. Then, in Windows High Contrast Mode, the color will be set to black and the svg will adjust accordingly.

Here's an example of one place where we fixed this — the logo in the global header on Khan Academy.

Before

Screenshot:

Screenshot of the global header at the top of the Khan Academy website. The header is white. In the center of the header is the Khan Academy logo icon, which is green. To the right of the logo icon is a blank space.

CSS:

.logoSvg {
    fill: #fff;
}

JSX:

return (
    <a aria-label="Khan Academy" href="/">
        <svg className="logoSvg" aria-hidden={true}>...</svg>
    </a>
);
After

Screenshot:

Screenshot of the global header at the top of the Khan Academy website. The header is white. In the center of the header is the Khan Academy logo icon, which is green. To the right of the logo icon is dark blue text that reads 'Khan Academy'.

CSS:

.logoLink {
    fill: #fff;
}
.logoSvg {
    fill: currentColor;
}

JSX:

return (
    <a className="logoLink" aria-label="Khan Academy" href="/">
        <svg className="logoSvg" aria-hidden={true}>...</svg>
    </a>
);

Border and outline colors

Use invisible borders and outlines to visually define edges of elements that are created through different background colors laid against each other. Since borders and outlines are automatically set to 100% opacity black, you can make them 0% opacity in default mode so they will only appear in Windows High Contrast Mode. Borders can have rounded edges and be restricted to specific sides, but they increase the size of the element. To avoid affecting the design in default mode, if you're retrofitting an existing design, you might want to use an outline.

This is something we encountered with our sign up/login modals, which are visually defined based on their light background colors against the dark partially-transparent backdrop.

(Side-note: Here you can see an example of a partially-transparent background color being set to white but with the same opacity as in the original CSS.)

Before

Screenshot:

Screenshot of a Khan Academy video page. The page is has a partially transparent white veil over it. In the center of the page is a big white rectangle. In the rectangle is a login form. The white rectangle has no outline.

After

Screenshot:

Screenshot of a Khan Academy video page. The page is has a partially transparent white veil over it. In the center of the page is a big white rectangle. In the rectangle is a login form. The white rectangle has a black outline.

CSS:

.modal {
    border: 1px solid rgba(0,0,0,0);
}

Summary

In the end, the best approach to accessibility that I've found is to follow the web development standards when possible.

  1. Use semantic HTML for elements and CSS for styling. If an image is decorative, use background-image CSS. If an image is imperative for all users to see, use an <img> or SVG element (with appropriate alt text). If the element links to something, use an <a> element. Following these standardized patterns will get you most of the way for Windows High Contrast Mode (as well as screen readers and keyboard navigation too).
  2. Be careful when overriding default styling. When you override the default styling for an element (for example the focus outline, text decoration, or border on interactive elements), it has an effect that may be invisible to you based on your specific testing environment. Consider making the style invisible using 0% opacity color instead of removing it altogether.

Questions / comments / sweet memes?

You can reach out to us on Twitter or send us a support ticket!

Thank you for reading this post (or at least scrolling to the end)! I hope that it's been educational to you in some way and helps in your journey to make the web a better place.

Onward!