Keeping up with Kibana: This week in Kibana for April 26, 2018

With the release of 6.0, Kibana took aim at providing a more inclusive, accessible design than in our previous versions. For those lost in terminology, accessible design refers to building a user interface that is usable by people of all backgrounds and abilities. It’s one of the core principles of the new Elastic UI Framework that powers Kibana’s design. In practice, meeting that goal means focusing on four main interface challenges.

  • Providing high-contrast, readable text
  • Providing color-blind safe UIs and visualizations
  • Making sure our UI is navigable by a keyboard
  • Using ARIA labels and semantic HTML to make the UI available through screen readers

In this post, we’ll discuss the first two points and how the Kibana design team meets these goals through tooling. It might sound fairly easy to do, or even boring, but by the end you’ll see just how much time (and math!) goes into calculating “high-contrast” and “color-blind safe” designs. As something to look forward to, I promise to also end the mystery of why Kibana is no longer pink!

Defining some rules

First, it’s important to note that when designing with accessibility in mind, the WCAG provides some pretty great resources and rulesets. Their documentation can get pretty dense, but when making decisions around accessibility, your first stop should always be to read the WCAG guidelines.

Two important guidelines to memorize are 1.4.1 and 1.4.3:

  • 1.4.3 states that color alone can not be the only way to convey visual information
  • 1.4.1 states that text must have a contrast level above 4.5:1

To understand what this means, let’s start with color first. Design is mostly the application of recognizable, repeatable patterns. A pattern you’ve probably run into, and which designers commonly employ, is that green is good, yellow is a warning, and red is danger. You see this pattern every time you drive a car. But what if you are one of the 1 in 12 men (it’s less common in women at 1 in 200) who are affected by Protanopia and have trouble distinguishing red from green?

Example of color blindness

Solving this problem is typically pretty easy. Rather than assuming people can see the difference in colors, we can be more explicit and use iconography and text as another way to convey our message. However, for more visually expressive elements, such as Kibana’s charting libraries, this is much harder to achieve. The legend on an area chart might define the purpose of a certain color, but that doesn’t do much good when you can’t tell the difference between the colors. In this situation, coming up with a color-safe palette (described in the next section) becomes more essential.

It all starts with the design

The Kibana design team uses Sketch to prototype our visual designs. It’s usually the first place we start laying out the interfaces that make up Kibana. Even this early in the process we want to think about accessibility and color. Luckily, we have some community tools to help us out.

In particular, we love Stark, a Sketch plugin and overlay that let’s us simulate various forms of vision impairment and color blindness. We use Stark often when making designs, and more importantly, picking colors for our palettes. Here’s an example of how it works:

Example of stark plugin

Using Stark we came up with the colors in the Elastic UI framework. I’d love to say that there was a a super scientific way we formulated these colors, but it was actually quite organic. We started with our branding colors as a seed, and then added and tweaked more colors. In the end, we had a full palette of 10 colorblind safe colors.

The ability to distinguish each color individually became harder and harder with each new color we added. That’s because the new color had to be visible against all the previous ones. Essentially, large palettes can never be colorblind proof and aiming for ten distinct colors is likely as far as you can take it. If you do need more colors, remember that luminosity (the perceived brightness) rather than hue (the actual color itself) is the better thing to adjust.

In code, color is math

Our Sketch mocks are just a rough idea of what our designs can be. They deal more with layout than implementation. It’s only when these designs translate to code that we really begin to understand how color works.

We build the Elastic UI Framework with Sass, which gives us a lot of control to variabilize and manipulate our colors. As a pretty hard rule of EUI, the only hex colors you’ll see in our code are defined as variables. Any other color must be derived, through color functions beyond that original set.

// Core colors
$colorPrimary: #0079a5 !default;
$colorSecondary: #017F75 !default;
$colorAccent: #DD0A73 !default;

// Health colors
$colorSuccess: $euiColorSecondary !default;
$colorDanger: #A30000 !default;
$colorWarning: #E5830E !default;

// And then are used using in individual components
.button--warning {
  background: $colorWarning;
}

// And often manipulated with math. This ends up being somewhat pastel
.callout--warning {
  background-color: tint($colorWarning, 90%);
}

That’s all good for dealing with individual colors and likely not too much different from what you’ve seen in other frameworks. But what happens when we use one color as a background and another as a foreground? How do we know if they work as far as accessibility goes? More interestingly, how can we make these colors always pass a contrast against each other?

Here we really leaned into Sass and made some functions that can do the following.

Calculate luminance

Luminance is the perceived brightness of a color. Unlike true brightness, it has less to do with the amount of white or black in a color, but how different colors like yellow look brighter than darker colors like purple. We use luminance to calculate true contrast as defined by the WCAG.

Note that calculating luminance is no joke from a math standpoint and there are a lot of bad calculations floating around on Google. In fact, the below function actually utilizes some additional math functions that Sass doesn't have access to. Rather than make this post overly long you can grab them over here to make this function work. For now though I just want to show that we need to calculate luminance to properly calculate contrast.

Example of color luminance)

// Sass doesn't provide a luminance check so we need to do it on our own.
@function luminance($color) {
  // Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
  $rgba: red($color), green($color), blue($color);
  $rgba2: ();

  @for $i from 1 through 3 {
    $rgb: nth($rgba, $i);
    $rgb: $rgb / 255;

    $rgb: if($rgb < .03928, $rgb / 12.92, pow(($rgb + .055) / 1.055, 2.4));

    $rgba2: append($rgba2, $rgb);
  }

  @return .2126 * nth($rgba2, 1) + .7152 * nth($rgba2, 2) + 0.0722 * nth($rgba2, 3);
}

Calculate contrast ratio

Contrast is a ratio that compares the luminance of one color against the luminance of another. A higher ratio means a higher contrast. In practice, you should always pair a high luminance color (like bright yellow) with a low luminance color (like dark blue). The hue of the color doesn’t matter--only luminance matters when making these calculations. As mentioned before and by WCAG 4.5 is the magic number we're looking for to make our text accessible.

Example of contrast ratios

// Given two colors: a forground and background, this will spit out the contrast ratio.
@function contrastRatio($background, $foreground) {
  $backgroundLum: luminance($background) + .05;
  $foregroundLum: luminance($foreground) + .05;

  @return max($backgroundLum, $foregroundLum) / min($backgroundLum, $foregroundLum);
}

While the above two calculations are interesting, we needed them so we could build the following more useful functions.

Choose light or dark text usage

Now that we know the contrast of colors we can now build out a function to help us make decisions when choosing colors. For example: given a specific background color should we use light or dark text?

// Given $color, decide whether $lightText or $darkText should be used as the text color

// ex: chooseLightOrDarkText(#EEE, #FFF, #000) would return #000 because it has a higher contrast than #FFF against a #EEE background.
@function chooseLightOrDarkText($background, $lightText, $darkText) {
  $lightContrast: contrastRatio($background, $lightText);
  $darkContrast: contrastRatio($background, $darkText);

  @if ($lightContrast > $darkContrast) {
    @return $lightText;
  }
  @else {
    @return $darkText;
  }
}

Use light or dark text

Force a given color to pass a contrast test

We know we want to pass a 4.5:1 contrast check for accessibility, but sometimes this is extremely hard when working with certain color hues. For example, yellow is very hard to render on white. This function allows us to pass a foreground color (like yellow) and a background color (like white) and adjust the foreground color darker until the contrast is high enough to pass. What we end up with is a darker shade of yellow (similar to mustard) that reads close to the intention of our original color but now passes an accessibility check.

Use light or dark text

// Given a $foreground and a $background, make the $foreground AA accessibility by slightly adjusting it till the contrast is high enough

// ex: makeContrastColor($ornage, #FFF) would continually shade the $orange until it had higher than 4.5 contrast on a white background.

@function makeHighContrastColor($foreground, $background) {
  $contrast: contrastRatio($foreground, $background);

  $highContrastTextColor: $foreground;

  @while ($contrast < 4.5) {
    $highContrastTextColor: shade($highContrastTextColor, 5%);
    $contrast: contrastRatio($highContrastTextColor, $background);
  }

  @return $highContrastTextColor;
}

Testing and awareness

The functions and mixins we provide are pretty automatic, but even then you must remember to use them. The engineering team at Elastic is large and they’re more likely to pull from the original palettes rather than use the Sass mixins. Because of this, we provide documentation and testing that illustrates our color contrasts.

Accessible color mapping

The above page takes the Elastic UI color palette, compares the colors in a grid, and gives a WCAG rating for each combination. This gives our engineers confidence in knowing which levels of gray to use for text, and which for purely decorative purposes.

Beyond that, we also use Chrome extensions to test contrast values in the browsers. Although these tools are by no means perfect, they do send up smoke on systems we need to keep an eye on.

So why not pink?

I promised an end to the mystery on why Kibana 6 stopped being so pink. Elastic’s brand of pink is a color that sits in between luminance extremes at 0.271. This makes it equally hard to pass a contrast check using either white or black foreground text. Instead we choose one of the brand colors (blue) that has a better range of luminosity. Blue can be recognizable with high and low luminance no matter how we shade it, and looks good with either light or dark text. Pink on the other hand requires a high luminance to be recognizable as pink and needs to shade darker (into a plum) to pass a contrast check against white. Put bluntly: blue is much easier to work with as far as accessibility is concerned.

Accessible color mapping