diff --git a/packages/appChrome/components/HeaderBar.tsx b/packages/appChrome/components/HeaderBar.tsx index 64861c012..24939741c 100644 --- a/packages/appChrome/components/HeaderBar.tsx +++ b/packages/appChrome/components/HeaderBar.tsx @@ -4,10 +4,9 @@ import { flex } from "../../shared/styles/styleUtils"; import { ThemeProvider, useTheme } from "@emotion/react"; import { headerBar } from "../style"; import { AppChromeTheme } from "../types"; -import { getCSSVarValue } from "../../utilities"; -import { themeBgPrimary } from "../../design-tokens/build/js/designTokens"; +import { greyLightLighten4 } from "../../design-tokens/build/js/designTokens"; -export const defaultBgColor = getCSSVarValue(themeBgPrimary); +export const defaultBgColor = greyLightLighten4; export const defaultHeaderPaddingHor = "none"; export const defaultHeaderPaddingVert = "none"; @@ -32,7 +31,7 @@ const StyledHeader = ({ children }: HeaderProps) => { const Header = ({ bgColor, children }: HeaderProps) => { const adjustedTheme = ancestorTheme => { return { - headerBackgroundColor: bgColor || getCSSVarValue(themeBgPrimary), + headerBackgroundColor: bgColor || greyLightLighten4, ...ancestorTheme }; }; diff --git a/packages/appChrome/components/Sidebar.tsx b/packages/appChrome/components/Sidebar.tsx index 0b70962f4..0518b4ac4 100644 --- a/packages/appChrome/components/Sidebar.tsx +++ b/packages/appChrome/components/Sidebar.tsx @@ -2,8 +2,10 @@ import * as React from "react"; import { cx } from "@emotion/css"; import { ThemeProvider, useTheme } from "@emotion/react"; import { sidebar, sidebarAnimator, sidebarContainer } from "../style"; -import { themeBgDisabled } from "../../design-tokens/build/js/designTokens"; -import getCSSVarValue from "../../utilities/getCSSVarValue"; +import { + greyLightLighten1, + greyLightLighten4 +} from "../../design-tokens/build/js/designTokens"; import { getResponsiveStyle } from "../../shared/styles/styleUtils"; import { AppChromeTheme } from "../types"; export interface SidebarProps { @@ -85,10 +87,9 @@ const Sidebar = ({ const adjustedTheme = ancestorTheme => { return { - sidebarBackgroundColor: bgColor || getCSSVarValue(themeBgDisabled), - // TODO update these with design tokens - itemActiveBackgroundColor: activeColor || "#D0D4D7", - itemHoverBackgroundColor: hoverColor || "#DDDFE2", + sidebarBackgroundColor: bgColor || greyLightLighten4, + itemActiveBackgroundColor: activeColor || greyLightLighten1, + itemHoverBackgroundColor: hoverColor || greyLightLighten1, sidebarWidth: defaultSidebarWidths, ...ancestorTheme }; diff --git a/packages/appChrome/style.ts b/packages/appChrome/style.ts index 21a1e829b..380088289 100644 --- a/packages/appChrome/style.ts +++ b/packages/appChrome/style.ts @@ -10,7 +10,7 @@ import { themeBgPrimary } from "../design-tokens/build/js/designTokens"; import { padding, tintContent } from "../shared/styles/styleUtils"; -import { pickHoverBg, pickReadableTextColor } from "../shared/styles/color"; +import { pickHoverBg, getTextColor } from "../shared/styles/color"; import getCSSVarValue from "../utilities/getCSSVarValue"; import { AppChromeTheme } from "./types"; import { @@ -45,7 +45,7 @@ export const appWrapper = css` export const headerBar = (theme: AppChromeTheme) => { const bgColor = theme.headerBackgroundColor || getCSSVarValue(themeBgPrimary); - const textColor = pickReadableTextColor( + const textColor = getTextColor( bgColor, getCSSVarValue(themeTextColorPrimary), getCSSVarValue(themeTextColorPrimaryInverted) @@ -75,7 +75,7 @@ export const sidebar = css` export const sidebarContainer = (theme: AppChromeTheme, isOpen?: boolean) => { const bgColor = theme.sidebarBackgroundColor || getCSSVarValue(themeBgDisabled); - const textColor = pickReadableTextColor( + const textColor = getTextColor( bgColor, getCSSVarValue(themeTextColorPrimary), getCSSVarValue(themeTextColorPrimaryInverted) @@ -84,7 +84,7 @@ export const sidebarContainer = (theme: AppChromeTheme, isOpen?: boolean) => { return css` background-color: ${bgColor}; transform: ${`translateX(${isOpen ? 0 : "-100%"})`}; - ${tintContent(textColor)}; + color: ${textColor}; `; }; @@ -135,7 +135,7 @@ export const sidebarNavItem = ( return css` background-color: ${itemBgColor}; color: ${isActive - ? pickReadableTextColor( + ? getTextColor( itemBgColor, getCSSVarValue(themeTextColorPrimary), getCSSVarValue(themeTextColorPrimaryInverted) diff --git a/packages/appChrome/tests/__snapshots__/Sidebar.test.tsx.snap b/packages/appChrome/tests/__snapshots__/Sidebar.test.tsx.snap index 02742465d..36626425a 100644 --- a/packages/appChrome/tests/__snapshots__/Sidebar.test.tsx.snap +++ b/packages/appChrome/tests/__snapshots__/Sidebar.test.tsx.snap @@ -3,13 +3,12 @@ exports[`Sidebar SidebarItem renders 1`] = ` .emotion-0 { - background-color: #E8EAED; + background-color: #F0F1F3; -webkit-transform: translateX(0); -moz-transform: translateX(0); -ms-transform: translateX(0); transform: translateX(0); color: #1B2029; - fill: #1B2029; height: 100%; overflow-x: hidden; -webkit-transition: width 150ms ease-in-out,-webkit-transform 150ms ease-in-out; @@ -45,7 +44,7 @@ exports[`Sidebar SidebarItem renders 1`] = ` } .emotion-2 { - background-color: #E8EAED; + background-color: #F0F1F3; cursor: pointer; opacity: 1; text-transform: capitalize; @@ -59,7 +58,7 @@ exports[`Sidebar SidebarItem renders 1`] = ` .emotion-2:hover, .emotion-2:focus, .emotion-2:focus-within { - background-color: #DDDFE2; + background-color: #DDE0E4; } .emotion-2:hover, @@ -351,13 +350,12 @@ exports[`Sidebar SidebarSubMenuItem renders 1`] = ` exports[`Sidebar renders 1`] = ` .emotion-0 { - background-color: #E8EAED; + background-color: #F0F1F3; -webkit-transform: translateX(0); -moz-transform: translateX(0); -ms-transform: translateX(0); transform: translateX(0); color: #1B2029; - fill: #1B2029; height: 100%; overflow-x: hidden; -webkit-transition: width 150ms ease-in-out,-webkit-transform 150ms ease-in-out; diff --git a/packages/shared/styles/color.ts b/packages/shared/styles/color.ts index 2c2b8d2a8..d3aab9077 100644 --- a/packages/shared/styles/color.ts +++ b/packages/shared/styles/color.ts @@ -100,6 +100,50 @@ export const pickReadableTextColor = ( : invertedTextOption; }; +/* + * This function will select a WCAG-compliant text color based on the background color. + * Reference: https://wunnle.com/dynamic-text-color-based-on-background + * TODO: We should deprecate pickReadableTextColor and update components that use it. + */ +export const getTextColor = ( + bgColor, + darkTextColor, + lightTextColor +): string => { + // Convert color from hex to RGB. + const getRGB = (c: string | number): number => { + return parseInt(c as string, 16) || (c as number); + }; + + // Convert to sRGB and apply gamma correction. + const getsRGB = (c: string): number => { + const rgb = getRGB(c) / 255; + return rgb <= 0.03928 ? rgb / 12.92 : Math.pow((rgb + 0.055) / 1.055, 2.4); + }; + + // Calculate relative luminance. + const getLuminance = (hexColor: string): number => { + return ( + 0.2126 * getsRGB(hexColor.slice(1, 3)) + + 0.7152 * getsRGB(hexColor.slice(3, 5)) + + 0.0722 * getsRGB(hexColor.slice(5, 7)) + ); + }; + + // Use relative luminance to compare color contrast. + const getContrast = (f: string, b: string): number => { + const L1 = getLuminance(f); + const L2 = getLuminance(b); + // Ratio formula is defined by WCAG guidelines + return (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05); + }; + + const LIGHT_COLOR = getContrast(bgColor, lightTextColor); + const DARK_COLOR = getContrast(bgColor, darkTextColor); + // Determine which color has the highest contrast ratio and return that color. + return LIGHT_COLOR > DARK_COLOR ? lightTextColor : darkTextColor; +}; + // Assumes we always want our default hover colors to be lower // contrast between it's background color export const pickHoverBg = (bgColor, baseHoverBg, invertedHoverBg) => {