Skip to content
This repository has been archived by the owner on Dec 1, 2019. It is now read-only.

Audit Color Combinations and Automatically Correct Color Contrast at User-Customizable Colors #284

Closed
celloexpressions opened this issue Sep 15, 2019 · 22 comments

Comments

@celloexpressions
Copy link

There have been several issues and PRs related to color options. Now that the initial implementation is mostly complete, let's evaluate whether Twenty Twenty provides an appropriate amount of customization without over-complicating the user experience, in accordance with the WordPress Philosophy.

We also need to review all combinations of text and background colors for:

  1. Color contrast ratios with the default colors.
  2. Maintaining color contrast where one of the colors is user-customizable.
  3. Maintaining a minimum contrast threshold when both of the colors are user-customizable (by adjusting the selected color(s) as required).

Ideally, the result will be that users can change all of the colors in the theme and that a minimum color contrast threshold will be maintained regardless of the user's combination of selected colors. The current default color palate is:

#fff - header/footer background
#f5efe0 - body background
#dcd7ca - borders
#cd2653 - accent
#6d6d6d - secondary text
#000 - text

image

Initial Proposal

Based on my audit of color usage, I would propose the following adjustments:

  1. Merge the cover template overlay background color option with the accent color option. This option defaults to the accent color already, and is likely workable with the selected accent color for a large percentage of users. The separate option doesn't pass the initial 80/20 test for "Decisions, not Options".
  2. Eliminate or simplify the cover template overlay text color option in favor of automatically-determined white or black. The image background makes it too difficult to determine actual contrast, but we can determine whether white or black will work best based on the combination of accent color and opacity. If we decide that this option needs to stay, we may want to simplify it to a preselected list of colors instead of an open-ended color picker. Without this adjustment, users will likely lean too heavily on an obscure text color rather than selecting an appropriate level of opacity for contrast. Without a text color option, users would have to adjust opacity to ensure visible readibility.
  3. Add an option to customize the header/footer background color. This is a big missing piece currently, and makes the theme substantially more flexible.
  4. Don't add any text color options as proposed in Add option to change text color #3.

With these adjustments, users can customize these colors:

  1. Hader/footer background color.
  2. Primary background color.
  3. Accent color.

And the theme would "calculate" selections for these colors when the defaults don't provide adequate contrast (via an extension of #249):

  1. Header/footer text color.
  2. Body text color.
  3. "Secondary" text color (default #6d6d6d) in the header/footer.
  4. "Secondary" text color (default #6d6d6d) in the body.
  5. Border color (#dcd7ca)
  6. Darkened/lightened accent color if/as required for contrast with the header background color.
  7. Darkened/lightened accent color if/as required for contrast with the body background color.

All of the calculated colors can be either a specific preset color or a color generated based on the user's selected colors.

Next Steps

  1. Make a decision on each proposed option change and finalize the list of user-customizable colors.
  2. Make a decision on which colors will be based on an adjustment to a user-selected color and which colors will be selected between binary options.
  3. Finish implementing color calculation library for use in calculating colors (see my comment on Auto-calculate background-color luminance (#a11y) #249).
  4. Build the full color patterns CSS in PHP based on all color definitions in style.css for the default and user-customizable colors.
  5. Implement automated contrast checking and selection of calculated colors in the PHP-based CSS generation.
  6. Consider saving the generated CSS to a theme mod rather than calculating it on every page (see the Fourteen Colors plugin).
  7. Implement a hybrid JS-swap and selective-refresh approach to instantly live-preview color customizations in the customizer (I will open a separate issue).
@aristath
Copy link
Member

aristath commented Sep 15, 2019

There's a lot that can be done for accessibility with colors... For example there shouldn't be ANY text-color controls in the customizer.
Even links-colors can be autocalculated, I've build a script that given a background-color and text-color calculates the colors for links to ensure WCAG compliance. It can even have a hue-only slider and the rest gets auto-calculated.
The hard part is not implementing these things, the hard part is imagining them and reaching a decision. 😉

@celloexpressions
Copy link
Author

The hue-only approach works very well because all of the logic can live directly in the CSS by using HSL colors (and swapping out the selected hue). However, this theme lends itself better to full color pickers based on the differences between the default colors (less monochromatic).

My leaning is essentially for color pickers for the background colors and the (opinionated-default) accent color, with everything else being generated. The generation logic should result in the current default color patters when the default colors are applied.

@joyously
Copy link

My leaning is to supply default colors that meet the a11y goal, and let the user change the colors to whatever they prefer.

@andersnoren
Copy link
Contributor

After talking this through, we've decided that we will implement the following color options/changes:

  • The accent color setting will be changed to a hue setting, similar to Twenty Nineteen
  • In addition to the core background color setting, we'll add a background color setting for the header and footer areas
  • The colors for the content, header and footer will be autocalculated based on the background color chosen for each area, to ensure a a11y friendly contrast
  • Body text is black/white depending on the background colors chosen

@aristath has offered to lead the implementation of these options, and @celloexpressions will assist with feedback.

@aristath aristath mentioned this issue Sep 18, 2019
ianbelanger79 pushed a commit that referenced this issue Sep 21, 2019
* Get the color properties we'll need

* Add isBackgroundLight & isBackgroundDark methods

* cleanup

* hslToRGB function

* More WIP

* Rebuilt it

* missed this

* add customizer.js file

* Changed accent-color control to hue.

* disable previous implementation for now

* Enqueue files

* notes

* Add twentytwenty_get_color_for_area function

* Change body & accent colors on the frontend

* Add script for live-update background-color

* Let there be light.

* Rename files & add some more inline docs

* use real functions instead of closures

* Use wp_localize_script

* Improve color calculations

* Rename file to color-calculations

* Revert "Rename file to color-calculations"

This reverts commit c3a8054.

* inline docs fix

* minor bugfix in case there was an error

* Add calcs for header colors

* Add styles for header

* typo

* bugfix for header colors on preview
Problem occured when when changing content bg-color

* Tweak the fallback calc

* pushed this one by mistake

* Improve color script consistency & performance

* Simplify colors script

* The TwentyTwenty_Color class is no longer required

* more color script tweaks - prefer AAA-compliant

* Create proper sanitization callback

* Remove old colorpicker, no longer used

* Add accent color to editor palette

* fix accent_accessible_colors setting default value

* Added header/footer background-color

* Rename file to color-calculations

* Remove priorities from controls because 10.

* Return false on twentytwenty_get_color_for_area

* create elements array

* shorten the array definitions

* Save the background-color value in the option

* auto-calculate secondary color

* rename key to "borders"

* added PHP implementation for css-output

* Save secondary color.

* better calculations for secondary color

* Add elements for secondary

* bugfix for secondary value (return string)

* fix sanitization method for new structure

* coding-standards fixes

* Add more colors to the palette

* Add body color styles to the editor

* tweak for editor title color

* Add support for dark-mode depending on background

* remove extra blank line - fails travis test

* coding-standards fixes

* fill missing default values

* fix lint issues
@joyously
Copy link

Testing with the latest master, I see two things:

  • default value of menu background color is not taken into account. As soon as I changed the body background color, the menu color popped in, but the text is not white like the 3 dots.
  • The page title is not being changed.
    color-selection

@aristath
Copy link
Member

aristath commented Sep 21, 2019

Thank you Joy! 👍
The issue is the array of elements here:

twentytwenty/functions.php

Lines 661 to 699 in 049e8fe

$elements = array(
'content' => array(
'accent' => array(
'color' => array( '.color-accent', '.color-accent-hover:hover', '.has-accent-color', '.has-drop-cap:not(:focus):first-letter', '.wp-block-pullquote:before' ),
'border-color' => array( 'blockquote', '.border-color-accent', '.border-color-accent-hover:hover' ),
'background' => array( 'button', '.button', '.faux-button', '.wp-block-button__link', '.wp-block-file__button', 'input[type="button"]', 'input[type="reset"]', 'input[type="submit"]' ),
'background-color' => array( '.bg-accent', '.bg-accent-hover:hover', '.has-accent-background-color', '.comment-reply-link', '.edit-comment-link' ),
'fill' => array( '.fill-children-accent', '.fill-children-accent *' ),
),
'background' => array(
'color' => array( 'button', '.button', '.faux-button', '.wp-block-button__link', '.wp-block-file__button', 'input[type="button"]', 'input[type="reset"]', 'input[type="submit"]', '.comment-reply-link', '.edit-comment-link' ),
'background' => array( '.singular .featured-media:before', '.wp-block-pullquote:before' ),
),
'text' => array(
'color' => array( 'body' ),
),
'secondary' => array(
'color' => array( 'cite', 'figcaption', '.wp-caption-text', '.post-meta', '.entry-content .wp-block-archives li', '.entry-content .wp-block-categories li', '.entry-content .wp-block-latest-posts li', '.wp-block-latest-comments__comment-date', '.wp-block-latest-posts__post-date', '.wp-block-embed figcaption', '.wp-block-image figcaption', '.wp-block-pullquote cite', '.comment-metadata', '.comment-respond .comment-notes', '.comment-respond .logged-in-as', '.pagination .dots' ),
),
),
'header-footer' => array(
'accent' => array(
'color' => array( '#site-header a', '#site-footer a' ),
'background' => array( 'social-icons a' ),
'background-color' => array( '.footer-social a' ),
),
'background' => array(
'color' => array( 'social-icons a', '.overlay-header:not(.showing-menu-modal) .header-inner', '.primary-menu ul', '.overlay-header.showing-menu-modal .header-inner' ),
'background' => array( '#site-header', '.menu-modal', '.menu-modal-inner', '.search-modal-inner', '.archive-header', '.singular .entry-header', '#site-footer' ),
),
'text' => array(
'background' => array( '.primary-menu ul' ),
'border-left-color' => array( '.primary-menu ul ul:after' ),
),
'secondary' => array(
'color' => array( '.site-description', '.toggle-inner .toggle-text', '.widget .post-date', '.widget .rss-date', '.widget_archive li', '.widget_categories li', '.widget_pages li', '.widget_meta li', '.widget_nav_menu li', '.powered-by-wordpress', '.to-the-top' ),
),
),
);

These will have to be reviewed and tweaked to fix any issues like the one you mentioned.
That array is used in the customizer for the live previews, as well as in custom-css.php for the frontend.
Please note that besides the accent, background, text and secondary there's also borders that can be used, I just didn't manage to do a complete clone of all the elements as that one was added last.

text is the color used for normal text, accent is for the accent colors, background uses the color selected for the background of that area, secondary is what you can find in style.css as #6d6d6d and borders in style.css is #dcd7ca.
So we should populate that array with elements from the main stylesheet 👍

@joyously
Copy link

Why is the accent color restricted to the same saturation as the background color? And if it is, can the selection shown in the control be updated to reflect that?
See here where the Accent is on yellow, but it only shows as yellow when the background is fully saturated.
colors-saturation

@carolinan
Copy link
Contributor

I made a small update here #440, but this can still be improved on, we might not want this level of specificity.

@aristath
Copy link
Member

Why is the accent color restricted to the same saturation as the background color? And if it is, can the selection shown in the control be updated to reflect that?

The accent color is not restricted to the same saturation.
Here's how it works:

  1. We get the background color.
  2. We get the hue for the accent from the Accent Color Hue control
  3. We build an array of about 280 colors using the selected hue. The "restrictions" here are:
  • Minimum saturation: 55
  • Maximum saturation: 90
  • Minimum lightness: 25
  • Maximum lightness: 75
  • Step: 2.5
    Using the above rules (you can see them here) we start building an array of HSL colors: [HUE, 55, 25], [HUE, 57.5, 25], [HUE, 57.5, 27,5], [HUE, 60, 27,5] and so on.
  1. We check individually the contrast of each of these colors with the background. If it's below 4.5, it doesn't make the list (threshold for WCAG compliance)
  2. We check the contrast of each of these colors with surrounding text. If it's below 3, it doesn't make the list (threshold for WCAG compliance).
  3. We check the array of colors that comply with our rules. If one (or more of them) have a 7:1 contrast with the background-color, then that's the list we'll be working with.
  4. Each color is assigned a score which is basically just the contrast of the color with the background, multiplied by the contrast of the color to surrounding text.
  5. The array of colors gets ordered by score
  6. We return the color with the highest score.

If yellow accent on gray doesn't return pure yellow but instead is mustard-y on most background-colors, that's because that is the most accessible color with that hue on that background.

@joyously
Copy link

I know it's complicated, but perhaps too complicated. The colors that need to be WCAG compliant should refer to text contrast with background, not just any two colors. But I can see that the Accent color is used as text color in some places and not in others, and on multiple backgrounds.
So, if you leave the calculation the same, can the Accent control hue strip actually reflect the restricted choices? It's very annoying to see the yellow and select it, but not get it.

@aristath
Copy link
Member

So, if you leave the calculation the same, can the Accent control hue strip actually reflect the restricted choices? It's very annoying to see the yellow and select it, but not get it.

I'm afraid not. The colors are selected based on their luminance contrast with the background. So it's not linear, it depends on the hue.
In order to change the hue strip to show a representation of what colors would look like we'd have to run the calculations for each hue every time the background color changes.
So that would be 280 * 360 = 100800 Color objects created, queried and calculated on the browser side for each change. Imagine dragging the colorpicker which would go through a few hundred background colors per second, then querying the colors, then changing the hue-strip.
It would be extremely resources-intensive.

Yellow in particular is the most problematic hue when it comes to luminance... it is the closest to white and poses a lot of challenges, it is the edge case.

There is no perfect solution.
This implementation restricts user options and enforces safe, sane and accessible colors.
It makes it easier to build a site that I can read.
Is it perfect? No. But I want to believe that it's better than the alternative. 👍

@aristath
Copy link
Member

The colors that need to be WCAG compliant should refer to text contrast with background, not just any two colors. But I can see that the Accent color is used as text color in some places and not in others, and on multiple backgrounds.

The accent colors are used primarily for links, buttons etc. Not just any text... That's why it's necessary to check the contrast against both the background and surrounding text.
As for the "multiple backgrounds", the colors used in the header & footer are calculated independently from the content. Since header/footer have a different background-color, calculations for these areas are done using that background-color.

@aristath
Copy link
Member

If we want to further tweak the color-selection, we can remove the surrounding-text check from these lines:

// Check a minimum of 4.5:1 contrast with the background and 3:1 with surrounding text.
if ( 4.5 > item.contrastBackground || 3 > item.contrastText ) {
return;
}

aristath added a commit to aristath/twentytwenty that referenced this issue Sep 21, 2019
carolinan pushed a commit that referenced this issue Sep 21, 2019
Adds elements that were using #dcd7ca in style.css to the array of elements to be affected by the a11y colors
@celloexpressions
Copy link
Author

I don't have any major objections to the implementation that has been merged here. It looks like some of the minor fixes and adjustments have already been worked out.

With yellow in particular, I would note that the background color needs to be dark to achieve contrast without getting a darker color than may be desired. This seems like expected behavior and encourages users to select combinations that work well together for contrast.

A few more things to consider before we close this out:

  1. Can we remove css transitions for colors in the customize preview? The speed of color updating lags on link elements and certain SVG icons currently because of thransition: all 0.15s linear on a and path elements. Alternatively, refine the all for transitions to only the properties that change on hover/focus states.
  2. A few areas that I'm seeing missing custom colors are: entry categories in archives, archive page numbers, captions (including in footer widgets), and all decorative border elements. I recommend going through all of style.css to collect color selectors to ensure that we don't miss any.
  3. Is there (or can we add) clear documentation for how to add selectors to the color patterns? Ideally we could include a reminder in styles.css that anything changing colors needs to be synced to the color patterns.
  4. Are there adequate filters to allow child themes to extend the color selectors without building their own implementation?
  5. Are all of the functions set up so that child themes can introduce additional color customization options without duplicating the parent theme's implementation?

If anyone has time to create an actual child theme to test the exstensibility of this feature, that's the best way to find problems with the technical implementation.

@aristath
Copy link
Member

Can we remove css transitions for colors in the customize preview? The speed of color updating lags on link elements and certain SVG icons currently because of thransition: all 0.15s linear on a and path elements. Alternatively, refine the all for transitions to only the properties that change on hover/focus states.

A few areas that I'm seeing missing custom colors are: entry categories in archives, archive page numbers, captions (including in footer widgets), and all decorative border elements. I recommend going through all of style.css to collect color selectors to ensure that we don't miss any.

I believe these 2 are already being taken care of in the main stylesheet and elements have already been added to the array.

Is there (or can we add) clear documentation for how to add selectors to the color patterns? Ideally we could include a reminder in styles.css that anything changing colors needs to be synced to the color patterns.

It's a single filter, twentytwenty_get_elements_array. Where would documentation for this be?

Are there adequate filters to allow child themes to extend the color selectors without building their own implementation?

Yes, they can change all elements using the twentytwenty_get_elements_array filter.

Are all of the functions set up so that child themes can introduce additional color customization options without duplicating the parent theme's implementation?

On the PHP side it should be fairly simple.
On the JS side I think we'd be able to do this using wp.hooks.addFilter and wp.hooks.applyFilters, but that would only work on WP 5.0+

@YanCol
Copy link
Contributor

YanCol commented Sep 30, 2019

Congrats for this amazing work on the color settings! 👏
Having the possibility for a child theme to introduce additional color customization options would be a nice feature.

@aristath
Copy link
Member

aristath commented Oct 3, 2019

Having the possibility for a child theme to introduce additional color customization options would be a nice feature.

Adding elements and styles for the colors that already get calculated is easy using the existing filters.
We already auto-calculate 4 different colors per-area... If a child theme wants to introduce a new auto-calculated color, that would mean that they need different calculations for their color. So if they know how to calculate colors, I think it's safe to assume they'll know how to inject them in the value too... Adding the value to the array is easier than everything else.
We can add a JS filter to make their life easier, but that would mean breaking compatibility with WP versions prior to 5.0 since wp.hooks didn't exist before that.

Developers can define elements that will be controlled by the existing colors. They can define new "areas" that will have their colors calculated by changing the twentyTwentyBgColors var, the one thing that requires some skill is calculating colors.

I'm not sure if we should do something to fix that.

@ianbelanger79
Copy link
Contributor

@aristath and @celloexpressions where are we at with this issue? Has it been fixed? Is there more work that needs to be done? Please let me know. Thanks

@joyously
Copy link

Hey, someone mentioned this on Slack today: https://stripe.com/es-mx/blog/accessible-color-systems

Could be good to get less muddy colors.

@pattonwebz
Copy link
Member

I feel like we currently have a fairly good system in place with the theme that makes color selections accessible with the options that are provided for color selections.

I am closing this issue here as I think this is pretty much done but if other things need adjusted we should open new tickets for the changes on trac.

@maxcady
Copy link

maxcady commented Nov 20, 2019

I think this is reducing accessibility to some color contrast rules set up by a function. What about red/green color blindness? This color slider gimmick is totally overrated if you ask me.

@aristath
Copy link
Member

@maxcady the function works using relative luminance which also takes into account the different ways people may perceive colors - including red and and green color blindness. It is what WCAG recommends, and in fact the algorithms used are the exact same ones from the WCAG recommendations.
Is it perfect? No, it's not. But then again nothing is. However, it's as close to accessible color selections as possible.
Not everyone will like them. It's impossible to build something that will satisfy everyone. But in the vast majority of cases this "gimmick" will help a lot of people. If you think it's overrated then by all means feel free to submit your improvements on Trac.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

9 participants