Skip to content

Commit

Permalink
Improve contrast with pervasive system accent colors.
Browse files Browse the repository at this point in the history
This attempts to guarantee color visibility/readability in many more
scenarios. To do this, PickGoogleColorTwoBackgrounds() is moved to ui::.

Bug: 1413984
Change-Id: Idd8842449848cf3989996ead099e9cfad3d41f3a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4283758
Reviewed-by: Thomas Lukaszewicz <tluk@chromium.org>
Commit-Queue: Peter Kasting <pkasting@chromium.org>
Auto-Submit: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1108711}
  • Loading branch information
pkasting authored and Chromium LUCI CQ committed Feb 23, 2023
1 parent 11340a2 commit b4470df
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 118 deletions.
54 changes: 35 additions & 19 deletions chrome/browser/ui/color/chrome_color_mixer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,9 @@ void AddChromeColorMixer(ui::ColorProvider* provider,
mixer[kColorDownloadShelfContentAreaSeparator] = ui::AlphaBlend(
kColorToolbarButtonIcon, kColorDownloadShelfBackground, 0x3A);
mixer[kColorDownloadShelfForeground] = {kColorToolbarText};
mixer[kColorDownloadStartedAnimationForeground] = {ui::kColorAccent};
mixer[kColorDownloadStartedAnimationForeground] =
PickGoogleColor(ui::kColorAccent, kColorDownloadShelfBackground,
color_utils::kMinimumVisibleContrastRatio);
mixer[kColorDownloadToolbarButtonActive] =
ui::PickGoogleColor(ui::kColorThrobber, kColorToolbar,
color_utils::kMinimumVisibleContrastRatio);
Expand All @@ -178,15 +180,19 @@ void AddChromeColorMixer(ui::ColorProvider* provider,
mixer[kColorDownloadToolbarButtonRingBackground] = {
SkColorSetA(kColorDownloadToolbarButtonInactive, 0x33)};
mixer[kColorExtensionDialogBackground] = {SK_ColorWHITE};
mixer[kColorExtensionIconBadgeBackgroundDefault] = {ui::kColorAccent};
mixer[kColorExtensionIconBadgeBackgroundDefault] =
PickGoogleColor(ui::kColorAccent, kColorToolbar,
color_utils::kMinimumVisibleContrastRatio);
mixer[kColorExtensionIconDecorationAmbientShadow] =
ui::SetAlpha(ui::kColorShadowBase, 0x26);
mixer[kColorExtensionIconDecorationBackground] = {SK_ColorWHITE};
mixer[kColorExtensionIconDecorationKeyShadow] =
ui::SetAlpha(ui::kColorShadowBase, 0x4D);
mixer[kColorExtensionMenuIcon] = {ui::kColorIcon};
mixer[kColorExtensionMenuIconDisabled] = {ui::kColorIconDisabled};
mixer[kColorExtensionMenuPinButtonIcon] = {ui::kColorAccent};
mixer[kColorExtensionMenuPinButtonIcon] = PickGoogleColor(
ui::kColorAccentWithGuaranteedContrastAtopPrimaryBackground,
ui::kColorMenuBackground, color_utils::kMinimumVisibleContrastRatio);
mixer[kColorExtensionMenuPinButtonIconDisabled] = ui::SetAlpha(
kColorExtensionMenuPinButtonIcon, gfx::kDisabledControlAlpha);
mixer[kColorExtensionsMenuHighlightedBackground] = {
Expand Down Expand Up @@ -241,21 +247,23 @@ void AddChromeColorMixer(ui::ColorProvider* provider,
mixer[kColorIntentPickerItemBackgroundHovered] = ui::SetAlpha(
ui::GetColorWithMaxContrast(ui::kColorDialogBackground), 0x0F); // 6%.
mixer[kColorIntentPickerItemBackgroundSelected] = ui::BlendForMinContrast(
ui::kColorDialogBackground, ui::kColorDialogBackground, ui::kColorAccent,
1.2);
ui::kColorDialogBackground, ui::kColorDialogBackground,
ui::kColorAccentWithGuaranteedContrastAtopPrimaryBackground, 1.2);
mixer[kColorLocationBarBackground] = {kColorToolbarBackgroundSubtleEmphasis};
mixer[kColorLocationBarBackgroundHovered] = {
kColorToolbarBackgroundSubtleEmphasisHovered};
mixer[kColorLocationBarBorder] = {SkColorSetA(SK_ColorBLACK, 0x4D)};
mixer[kColorLocationBarBorderOpaque] =
ui::GetResultingPaintColor(kColorLocationBarBorder, kColorToolbar);
mixer[kColorMediaRouterIconActive] = {ui::kColorAccent};
mixer[kColorMediaRouterIconActive] =
PickGoogleColor(ui::kColorAccent, kColorToolbar,
color_utils::kMinimumVisibleContrastRatio);
mixer[kColorMediaRouterIconWarning] = {ui::kColorAlertMediumSeverity};
mixer[kColorNewTabButtonBackgroundFrameActive] = {
kColorTabBackgroundInactiveFrameActive};
mixer[kColorNewTabButtonBackgroundFrameInactive] = {
kColorTabBackgroundInactiveFrameInactive};
mixer[kColorNewTabButtonFocusRing] = PickGoogleColorTwoBackgrounds(
mixer[kColorNewTabButtonFocusRing] = ui::PickGoogleColorTwoBackgrounds(
ui::kColorFocusableBorderFocused,
ui::GetResultingPaintColor(kColorNewTabButtonBackgroundFrameActive,
ui::kColorFrameActive),
Expand Down Expand Up @@ -318,9 +326,13 @@ void AddChromeColorMixer(ui::ColorProvider* provider,
mixer[kColorProfilesReauthDialogBorder] = {SK_ColorWHITE};
mixer[kColorQrCodeBackground] = {SK_ColorWHITE};
mixer[kColorQrCodeBorder] = {ui::kColorMidground};
mixer[kColorQuickAnswersReportQueryButtonBackground] =
ui::SetAlpha(ui::kColorAccent, 0x0A);
mixer[kColorQuickAnswersReportQueryButtonForeground] = {ui::kColorAccent};
mixer[kColorQuickAnswersReportQueryButtonBackground] = ui::SetAlpha(
ui::kColorAccentWithGuaranteedContrastAtopPrimaryBackground, 0x0A);
mixer[kColorQuickAnswersReportQueryButtonForeground] = PickGoogleColor(
ui::kColorAccentWithGuaranteedContrastAtopPrimaryBackground,
ui::GetResultingPaintColor(kColorQuickAnswersReportQueryButtonBackground,
ui::kColorPrimaryBackground),
color_utils::kMinimumReadableContrastRatio);
mixer[kColorScreenshotCapturedImageBackground] = {ui::kColorBubbleBackground};
mixer[kColorScreenshotCapturedImageBorder] = {ui::kColorMidground};
mixer[kColorSidePanelBackground] = {kColorToolbar};
Expand Down Expand Up @@ -372,10 +384,10 @@ void AddChromeColorMixer(ui::ColorProvider* provider,
mixer[kColorTabCloseButtonFocusRingInactive] = ui::PickGoogleColor(
ui::kColorFocusableBorderFocused, kColorTabBackgroundInactiveFrameActive,
color_utils::kMinimumVisibleContrastRatio);
mixer[kColorTabFocusRingActive] = PickGoogleColorTwoBackgrounds(
mixer[kColorTabFocusRingActive] = ui::PickGoogleColorTwoBackgrounds(
ui::kColorFocusableBorderFocused, kColorTabBackgroundActiveFrameActive,
ui::kColorFrameActive, color_utils::kMinimumVisibleContrastRatio);
mixer[kColorTabFocusRingInactive] = PickGoogleColorTwoBackgrounds(
mixer[kColorTabFocusRingInactive] = ui::PickGoogleColorTwoBackgrounds(
ui::kColorFocusableBorderFocused, kColorTabBackgroundInactiveFrameActive,
ui::kColorFrameActive, color_utils::kMinimumVisibleContrastRatio);

Expand Down Expand Up @@ -516,16 +528,18 @@ void AddChromeColorMixer(ui::ColorProvider* provider,
mixer[kColorTabStrokeFrameActive] = {kColorToolbarTopSeparatorFrameActive};
mixer[kColorTabStrokeFrameInactive] = {
kColorToolbarTopSeparatorFrameInactive};
mixer[kColorTabstripLoadingProgressBackground] =
ui::AlphaBlend(ui::kColorAccent, kColorToolbar, 0x32);
mixer[kColorTabstripLoadingProgressForeground] = {ui::kColorAccent};
mixer[kColorTabstripLoadingProgressBackground] = ui::AlphaBlend(
kColorTabstripLoadingProgressForeground, kColorToolbar, 0x32);
// 4.5 matches the default light theme contrast of accent-against-toolbar.
mixer[kColorTabstripLoadingProgressForeground] =
PickGoogleColor(ui::kColorAccent, kColorToolbar, 4.5f);
mixer[kColorTabstripScrollContainerShadow] =
ui::SetAlpha(ui::kColorShadowBase, 0x4D);
mixer[kColorTabThrobber] = {ui::kColorThrobber};
mixer[kColorTabThrobberPreconnect] = {ui::kColorThrobberPreconnect};
mixer[kColorThumbnailTabBackground] = ui::BlendForMinContrast(
ui::kColorAccent, ui::kColorFrameActive, absl::nullopt,
color_utils::kMinimumVisibleContrastRatio);
mixer[kColorThumbnailTabBackground] =
ui::PickGoogleColor(ui::kColorAccent, ui::kColorFrameActive,
color_utils::kMinimumVisibleContrastRatio);
mixer[kColorThumbnailTabForeground] =
ui::GetColorWithMaxContrast(kColorThumbnailTabBackground);
mixer[kColorThumbnailTabStripBackgroundActive] = {ui::kColorFrameActive};
Expand Down Expand Up @@ -623,7 +637,9 @@ void AddChromeColorMixer(ui::ColorProvider* provider,
ui::kColorFrameInactive);
mixer[kColorWebAuthnBackArrowButtonIcon] = {ui::kColorIcon};
mixer[kColorWebAuthnBackArrowButtonIconDisabled] = {ui::kColorIconDisabled};
mixer[kColorWebAuthnPinTextfieldBottomBorder] = {ui::kColorAccent};
mixer[kColorWebAuthnPinTextfieldBottomBorder] =
PickGoogleColor(ui::kColorAccent, ui::kColorDialogBackground,
color_utils::kMinimumVisibleContrastRatio);
mixer[kColorWebAuthnProgressRingBackground] = ui::SetAlpha(
kColorWebAuthnProgressRingForeground, gfx::kGoogleGreyAlpha400);
mixer[kColorWebAuthnProgressRingForeground] = {ui::kColorThrobber};
Expand Down
26 changes: 0 additions & 26 deletions chrome/browser/ui/color/chrome_color_provider_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,32 +96,6 @@ SkColor GetToolbarTopSeparatorColor(SkColor toolbar_color,
frame_color);
}

ui::ColorTransform PickGoogleColorTwoBackgrounds(
ui::ColorTransform fg_transform,
ui::ColorTransform bg_a_transform,
ui::ColorTransform bg_b_transform,
float contrast_threshold) {
const auto generator =
[](ui::ColorTransform fg_transform, ui::ColorTransform bg_a_transform,
ui::ColorTransform bg_b_transform, float contrast_threshold,
SkColor input_color, const ui::ColorMixer& mixer) {
const SkColor fg_color = fg_transform.Run(input_color, mixer);
const SkColor bg_a_color = bg_a_transform.Run(input_color, mixer);
const SkColor bg_b_color = bg_b_transform.Run(input_color, mixer);
const SkColor result_color = color_utils::PickGoogleColor(
fg_color, bg_a_color, bg_b_color, contrast_threshold);
DVLOG(2) << "ColorTransform PickGoogleColorTwoBackgrounds:"
<< " Foreground Color: " << ui::SkColorName(fg_color)
<< " Background Color A: " << ui::SkColorName(bg_a_color)
<< " Background Color B: " << ui::SkColorName(bg_b_color)
<< " Result Color: " << ui::SkColorName(result_color);
return result_color;
};
return base::BindRepeating(generator, std::move(fg_transform),
std::move(bg_a_transform),
std::move(bg_b_transform), contrast_threshold);
}

bool ShouldApplyHighContrastColors(const ui::ColorProviderManager::Key& key) {
// Only apply custom high contrast handling on platforms where we are not
// using the system theme for high contrast.
Expand Down
8 changes: 0 additions & 8 deletions chrome/browser/ui/color/chrome_color_provider_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

#include "ui/color/color_id.h"
#include "ui/color/color_provider_manager.h"
#include "ui/color/color_provider_utils.h"
#include "ui/color/color_transform.h"
#include "ui/gfx/color_utils.h"

// Converts ColorId if |color_id| is in CHROME_COLOR_IDS.
Expand All @@ -30,12 +28,6 @@ color_utils::HSL GetThemeTint(int id, const ui::ColorProviderManager::Key& key);
// the foreground tab is the most important).
SkColor GetToolbarTopSeparatorColor(SkColor toolbar_color, SkColor frame_color);

ui::ColorTransform PickGoogleColorTwoBackgrounds(
ui::ColorTransform fg_transform,
ui::ColorTransform bg_a_transform,
ui::ColorTransform bg_b_transform,
float contrast_threshold);

// Returns true if we should apply chrome high contrast colors for the `key`.
bool ShouldApplyHighContrastColors(const ui::ColorProviderManager::Key& key);

Expand Down
2 changes: 1 addition & 1 deletion chrome/browser/ui/color/new_tab_page_color_mixer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ void AddGeneratedThemeComprehensiveColors(ui::ColorMixer& mixer) {
/* 10% opacity */ 0.1 * SK_AlphaOPAQUE),
ui::SetAlpha(gfx::kGoogleGrey900,
/* 10% opacity */ 0.1 * SK_AlphaOPAQUE));
mixer[kColorNewTabPageFocusRing] = PickGoogleColorTwoBackgrounds(
mixer[kColorNewTabPageFocusRing] = ui::PickGoogleColorTwoBackgrounds(
ui::kColorFocusableBorderFocused, element_background_color,
kColorNewTabPageBackground, color_utils::kMinimumVisibleContrastRatio);

Expand Down
3 changes: 1 addition & 2 deletions docs/ui/learn/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ and hidden when focus is gone. It is often drawn by a
handles drawing a colored border along a path. When creating focus rings, it's
important to color them such that they contrast with both the color inside and
the color outside the ring; see the uses of the
[`PickGoogleColorTwoBackgrounds()`](/chrome/browser/ui/color/chrome_color_mixer.cc)
function.
[`PickGoogleColorTwoBackgrounds`](/ui/color/color_transform.cc) function.

### HWND

Expand Down
1 change: 1 addition & 0 deletions ui/color/color_id.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@
/* If changing the variable name, the variable name in the test needs to */ \
/* be changed as well. */ \
E_CPONLY(kColorAccent) \
E_CPONLY(kColorAccentWithGuaranteedContrastAtopPrimaryBackground) \
E_CPONLY(kColorAlertHighSeverity) \
E_CPONLY(kColorAlertLowSeverity) \
E_CPONLY(kColorAlertMediumSeverity) \
Expand Down
31 changes: 31 additions & 0 deletions ui/color/color_transform.cc
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,37 @@ ColorTransform PickGoogleColor(ColorTransform foreground_transform,
std::move(background_transform), min_contrast);
}

ColorTransform PickGoogleColorTwoBackgrounds(
ColorTransform foreground_transform,
ColorTransform background_a_transform,
ColorTransform background_b_transform,
float min_contrast) {
const auto generator = [](ui::ColorTransform foreground_transform,
ui::ColorTransform background_a_transform,
ui::ColorTransform background_b_transform,
float min_contrast, SkColor input_color,
const ui::ColorMixer& mixer) {
const SkColor foreground_color =
foreground_transform.Run(input_color, mixer);
const SkColor background_a_color =
background_a_transform.Run(input_color, mixer);
const SkColor background_b_color =
background_b_transform.Run(input_color, mixer);
const SkColor result_color = color_utils::PickGoogleColorTwoBackgrounds(
foreground_color, background_a_color, background_b_color, min_contrast);
DVLOG(2) << "ColorTransform PickGoogleColor:"
<< " Foreground Color: " << ui::SkColorName(foreground_color)
<< " Background Color A: " << ui::SkColorName(background_a_color)
<< " Background Color B: " << ui::SkColorName(background_b_color)
<< " Min Contrast: " << base::NumberToString(min_contrast)
<< " Result Color: " << ui::SkColorName(result_color);
return result_color;
};
return base::BindRepeating(generator, std::move(foreground_transform),
std::move(background_a_transform),
std::move(background_b_transform), min_contrast);
}

ColorTransform HSLShift(ColorTransform color, color_utils::HSL hsl) {
const auto generator = [](ColorTransform transform, color_utils::HSL hsl,
SkColor input_color, const ColorMixer& mixer) {
Expand Down
9 changes: 9 additions & 0 deletions ui/color/color_transform.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ ColorTransform PickGoogleColor(
ColorTransform background_transform = FromTransformInput(),
float min_contrast = 0.0f);

// Like the version above, but attempts to contrast sufficiently against both
// supplied backgrounds.
COMPONENT_EXPORT(COLOR)
ColorTransform PickGoogleColorTwoBackgrounds(
ColorTransform foreground_transform,
ColorTransform background_a_transform,
ColorTransform background_b_transform,
float min_contrast);

// A transform that returns the HSL shifted color given the input color.
COMPONENT_EXPORT(COLOR)
ColorTransform HSLShift(ColorTransform color, color_utils::HSL hsl);
Expand Down
62 changes: 18 additions & 44 deletions ui/color/core_default_color_mixer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,6 @@

namespace ui {

namespace {

// TODO(pkasting): Construct colors from contrast ratios
// TODO(pkasting): Construct palette from accent, key, tint/bg, shade/fg colors

ColorTransform GoogleColorWithContrastRatio(ColorTransform foreground_transform,
ColorTransform background_transform,
float contrast_ratio) {
const auto generator = [](ColorTransform foreground_transform,
ColorTransform background_transform,
float contrast_ratio, SkColor input_color,
const ColorMixer& mixer) {
const SkColor foreground_color =
foreground_transform.Run(input_color, mixer);
const SkColor background_color =
background_transform.Run(input_color, mixer);
contrast_ratio *=
color_utils::GetContrastRatio(foreground_color, background_color);
const SkColor result_color = color_utils::PickGoogleColor(
foreground_color, background_color, contrast_ratio);
DVLOG(2) << "ColorTransform GoogleColorWithContrastRatio:"
<< " FG Transform Color: " << SkColorName(foreground_color)
<< " BG Transform Color: " << SkColorName(background_color)
<< " Contrast Ratio: " << base::NumberToString(contrast_ratio)
<< " Result Color: " << SkColorName(result_color);
return result_color;
};
return base::BindRepeating(generator, std::move(foreground_transform),
std::move(background_transform), contrast_ratio);
}

} // namespace

void AddCoreDefaultColorMixer(ColorProvider* provider,
const ColorProviderManager::Key& key) {
const bool dark_mode =
Expand All @@ -60,24 +27,29 @@ void AddCoreDefaultColorMixer(ColorProvider* provider,
<< (dark_mode ? "Dark" : "Light") << " window.";
ColorMixer& mixer = provider->AddMixer();
mixer[kColorAccent] = {dark_mode ? gfx::kGoogleBlue300 : gfx::kGoogleBlue600};
// 4.5 matches the default light theme contrast of
// accent-against-primary-background.
mixer[kColorAccentWithGuaranteedContrastAtopPrimaryBackground] =
PickGoogleColor(kColorAccent, kColorPrimaryBackground, 4.5f);
mixer[kColorAlertHighSeverity] = {dark_mode ? gfx::kGoogleRed300
: gfx::kGoogleRed600};
mixer[kColorAlertLowSeverity] = {dark_mode ? gfx::kGoogleGreen300
: gfx::kGoogleGreen700};
mixer[kColorAlertMediumSeverity] = {dark_mode ? gfx::kGoogleYellow300
: gfx::kGoogleYellow700};
mixer[kColorDisabledForeground] = BlendForMinContrast(
gfx::kGoogleGrey600, kColorPrimaryBackground, kColorPrimaryForeground);
mixer[kColorDisabledForeground] =
PickGoogleColor(gfx::kGoogleGrey600, kColorPrimaryBackground,
color_utils::kMinimumReadableContrastRatio);
mixer[kColorEndpointBackground] =
GetColorWithMaxContrast(kColorEndpointForeground);
mixer[kColorEndpointForeground] =
GetColorWithMaxContrast(kColorPrimaryBackground);
// This produces light and dark item highlight colors of blue 500 and 400,
// respectively.
mixer[kColorItemHighlight] = GoogleColorWithContrastRatio(
kColorAccent, kColorPrimaryBackground, 0.75f);
mixer[kColorItemHighlight] =
PickGoogleColor(kColorAccent, kColorPrimaryBackground,
color_utils::kMinimumVisibleContrastRatio);
mixer[kColorItemSelectionBackground] =
AlphaBlend(kColorAccent, kColorPrimaryBackground, 0x3C);
AlphaBlend(kColorAccentWithGuaranteedContrastAtopPrimaryBackground,
kColorPrimaryBackground, 0x3C);
mixer[kColorMenuSelectionBackground] =
AlphaBlend(kColorEndpointForeground, kColorPrimaryBackground,
gfx::kGoogleGreyAlpha200);
Expand All @@ -89,12 +61,14 @@ void AddCoreDefaultColorMixer(ColorProvider* provider,
: gfx::kGoogleGrey900};
mixer[kColorSecondaryForeground] = {dark_mode ? gfx::kGoogleGrey500
: gfx::kGoogleGrey700};
mixer[kColorSubtleAccent] = AlphaBlend(kColorAccent, kColorPrimaryBackground,
gfx::kGoogleGreyAlpha400);
mixer[kColorSubtleAccent] =
AlphaBlend(kColorAccentWithGuaranteedContrastAtopPrimaryBackground,
kColorPrimaryBackground, gfx::kGoogleGreyAlpha400);
mixer[kColorSubtleEmphasisBackground] =
BlendTowardMaxContrast(kColorPrimaryBackground, gfx::kGoogleGreyAlpha100);
mixer[kColorTextSelectionBackground] = AlphaBlend(
kColorAccent, kColorPrimaryBackground, gfx::kGoogleGreyAlpha500);
mixer[kColorTextSelectionBackground] =
AlphaBlend(kColorAccentWithGuaranteedContrastAtopPrimaryBackground,
kColorPrimaryBackground, gfx::kGoogleGreyAlpha500);
mixer[kColorTextSelectionForeground] =
GetColorWithMaxContrast(kColorTextSelectionBackground);
}
Expand Down

0 comments on commit b4470df

Please sign in to comment.