bootstrap: Color combinations that fail WCAG 2 AA 4.5:1 contrast ratio

Doubt that all of these will be tackled, but for reference (following https://github.com/twbs/bootstrap/pull/25123 which at least fixed one of the badly borked ones):

From http://getbootstrap.com/docs/4.0/getting-started/theming/

Blue Foreground: #FFFFFF Background: #007BFF The contrast ratio is: 4.0:1 Text failed at Level AA

Indigo Foreground: #FFFFFF Background: #6610F2 The contrast ratio is: 7.2:1 Text passed at Level AA

Purple Foreground: #FFFFFF Background: #6F42C1 The contrast ratio is: 6.5:1 Text passed at Level AA

Pink Foreground: #FFFFFF Background: #E83E8C The contrast ratio is: 3.8:1 Text failed at Level AA

Red Foreground: #FFFFFF Background: #DC3545 The contrast ratio is: 4.5:1 Text passed at Level AA

Orange Foreground: #212529 Background: #FD7E14 The contrast ratio is: 6.0:1 Text passed at Level AA

Yellow Foreground: #212529 Background: #FFC107 The contrast ratio is: 9.5:1 Text passed at Level AA

Green Foreground: #FFFFFF Background: #28A745 The contrast ratio is: 3.1:1 Text failed at Level AA

Teal Foreground: #FFFFFF Background: #20C997 The contrast ratio is: 2.1:1 Text failed at Level AA

Cyan Foreground: #FFFFFF Background: #17A2B8 The contrast ratio is: 3.0:1 Text failed at Level AA

From http://getbootstrap.com/docs/4.0/utilities/colors/

.text-primary Foreground: #007BFF Background: #FFFFFF The contrast ratio is: 4.0:1 Text failed at Level AA

.text-secondary Foreground: #6C757D Background: #FFFFFF The contrast ratio is: 4.7:1 Text passed at Level AA

.text-success Foreground: #28A745 Background: #FFFFFF The contrast ratio is: 3.1:1 Text failed at Level AA

.text-danger Foreground: #DC3545 Background: #FFFFFF The contrast ratio is: 4.5:1 Text passed at Level AA

.text-warning Foreground: #FFC107 Background: #FFFFFF The contrast ratio is: 1.6:1 Text failed at Level AA

.text-info Foreground: #17A2B8 Background: #FFFFFF The contrast ratio is: 3.0:1 Text failed at Level AA

.text-light Foreground: #F8F9FA Background: #343A40 The contrast ratio is: 10.9:1 Text passed at Level AA

.text-dark Foreground: #343A40 Background: #FFFFFF The contrast ratio is: 11.5:1 Text passed at Level AA

.text-muted Foreground: #6C757D Background: #FFFFFF The contrast ratio is: 4.7:1 Text passed at Level AA

.text-white Foreground: #FFFFFF Background: #343A40 The contrast ratio is: 11.5:1 Text passed at Level AA

.bg-primary Foreground: #FFFFFF Background: #007BFF The contrast ratio is: 4.0:1 Text failed at Level AA

.bg-secondary Foreground: #FFFFFF Background: #6C757D The contrast ratio is: 4.7:1 Text passed at Level AA

.bg-success Foreground: #FFFFFF Background: #28A745 The contrast ratio is: 3.1:1 Text failed at Level AA

.bg-danger Foreground: #FFFFFF Background: #DC3545 The contrast ratio is: 4.5:1 Text passed at Level AA

.bg-warning Foreground: #343A40 Background: #FFC107 The contrast ratio is: 7.1:1 Text passed at Level AA

.bg-info Foreground: #FFFFFF Background: #17A2B8 The contrast ratio is: 3.0:1 Text failed at Level AA

.bg-dark Foreground: #FFFFFF Background: #343A40 The contrast ratio is: 11.5:1 Text passed at Level AA

.bg-white Foreground: #343A40 Background: #FFFFFF The contrast ratio is: 11.5:1 Text passed at Level AA

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 8
  • Comments: 20 (11 by maintainers)

Most upvoted comments

(and yes, i did collate that list manually, as i know how to party on a friday night šŸ˜‰ )

In our extension of Bootstrap, we’ve added functions to lighten or darken colors until they meet minimum contrast, and updated the mixins to use those functions.

I’d love to see an option for $enable-force-accessible-contrast that would automatically generate accessible versions of components if set to true, regardless of what color was passed into the relevant mixins.

If that’s of interest, I’ll share some of our code.

Yes sorry, should have been clearer when closing - a lot of the combos were fairly ā€œlockedā€ for v4, and it would have been too much of a breaking change. We opted to focus on v5 for the full fixes.

I’m pressed for time at the moment, but I’ll try to write something longer this weekend.

This function is doing a lot of the work for us:

/**
 * Incrementally darkens or lightens a color to  meet color contrast requirements
 */
@function find-contrasting-color($color, $base: $body-bg, $minimum-contrast-ratio: 7) {
  $output-color: $color;
  $color-mix: choose-contrast-color($base); // returns white or black
  $contrast-ratio: contrast($output-color, $base); // custom function to calculate contrast ratio

  @while $contrast-ratio < $minimum-contrast-ratio {
    $output-color: mix($color-mix, $output-color, 1%);
    $contrast-ratio: contrast($output-color, $base);
  }

  @return $output-color;
}

We’ve written our own alert mixin like so:

@mixin custom-alert-variant($color) {
  $bg-color: color-level($color, $alert-bg-level);
  $txt-color: find-contrasting-color($color, $bg-color, 4.5);
  $border-color: find-contrasting-color($color, $bg-color, 3);

  @include gradient-bg($bg-color);
  color: $txt-color;
  border-color: $border-color;

  hr {
    border-top-color: $border-color;
  }

  .alert-link {
    color: $txt-color;
  }
}

This should force all of the alert variants to be accessible no matter what color is picked in the theme. Out of the box, the .alert-light styles don’t have enough contrast. Here’s the output for the out of the box theme value for .alert-light using our mixin instead:

.alert-light {
  background-color: #fefefe;
  color: #757576;
  border-color: #949495; }
  .alert-light hr {
    border-top-color: #949495; }
  .alert-light .alert-link {
    color: #757576; }

We’re expecting to have a lot of our developers throwing God knows what colors into the theme, so this the basic approach for how we’re trying to protect them from themselves.