Skip to content

Commit

Permalink
feat(tags): new light/dark mode colors (#1809)
Browse files Browse the repository at this point in the history
  • Loading branch information
jzempel committed May 23, 2024
1 parent 630d9f3 commit e3f1533
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 85 deletions.
37 changes: 0 additions & 37 deletions packages/tags/src/elements/Tag.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import React from 'react';
import { render, renderRtl } from 'garden-test-utils';
import { PALETTE_V8 } from '@zendeskgarden/react-theming';
import { Tag } from './Tag';

describe('Tag', () => {
Expand All @@ -23,40 +22,4 @@ describe('Tag', () => {

expect(container.firstChild).toBe(ref.current);
});

describe('hue', () => {
it('renders the hue provided', () => {
[
'grey',
'blue',
'kale',
'red',
'green',
'fuschia',
'pink',
'crimson',
'orange',
'lemon',
'lime',
'mint',
'teal',
'azure',
'royal',
'purple'
].forEach(color => {
const { container } = render(<Tag hue={color as any} />);

expect(container.firstChild).toHaveStyleRule(
'background-color',
(PALETTE_V8 as any)[color][600]
);
});
});

it('handles yellow hue with specialized shading', () => {
const { container } = render(<Tag hue="yellow" />);

expect(container.firstChild).toHaveStyleRule('background-color', PALETTE_V8.yellow[400]);
});
});
});
8 changes: 6 additions & 2 deletions packages/tags/src/styled/StyledClose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const StyledClose = styled.button.attrs<unknown>({
align-items: center;
justify-content: center;
transition: opacity 0.25s ease-in-out;
opacity: 0.8;
opacity: ${props => props.theme.opacity[1000]};
border: 0; /* [1] */
background: transparent; /* [1] */
cursor: pointer;
Expand All @@ -34,13 +34,17 @@ export const StyledClose = styled.button.attrs<unknown>({
appearance: none; /* [1] */
&:hover {
opacity: 0.9;
opacity: ${props => props.theme.opacity[1100]};
}
&:focus {
outline: none;
}
&:active {
opacity: ${props => props.theme.opacity[1200]};
}
${props => retrieveComponentStyles(COMPONENT_ID, props)};
`;

Expand Down
89 changes: 75 additions & 14 deletions packages/tags/src/styled/StyledTag.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*/

import React from 'react';
import { render, renderRtl } from 'garden-test-utils';
import { PALETTE, getColorV8 } from '@zendeskgarden/react-theming';
import { render, renderDark, renderRtl } from 'garden-test-utils';
import { PALETTE } from '@zendeskgarden/react-theming';
import { StyledTag } from './StyledTag';

describe('StyledTag', () => {
Expand Down Expand Up @@ -82,32 +82,93 @@ describe('StyledTag', () => {
});

describe('hue', () => {
it('renders using a default neutral hue', () => {
const { container } = render(<StyledTag />);
const color = getColorV8('neutralHue', 200);
it.each([['light'], ['dark']])('renders using a default hue for %s mode', mode => {
const renderFn = mode === 'light' ? render : renderDark;
const { container } = renderFn(<StyledTag />);
const backgroundColor = mode === 'dark' ? PALETTE.grey[800] : PALETTE.grey[200];
const foregroundColor = mode === 'dark' ? PALETTE.grey[300] : PALETTE.grey[900];

expect(container.firstChild).toHaveStyleRule('background-color', backgroundColor);
expect(container.firstChild).toHaveStyleRule('color', foregroundColor);
});

it.each([['light'], ['dark']])('renders using a "grey" hue in %s mode', mode => {
const renderFn = mode === 'light' ? render : renderDark;
const { container } = renderFn(<StyledTag hue="grey" />);
const backgroundColor = mode === 'dark' ? PALETTE.grey[300] : PALETTE.grey[700];
const foregroundColor = mode === 'dark' ? PALETTE.grey[1100] : PALETTE.white;

expect(container.firstChild).toHaveStyleRule('background-color', backgroundColor);
expect(container.firstChild).toHaveStyleRule('color', foregroundColor);
});

it.each([['light'], ['dark']])('renders using a "blue" hue in %s mode', mode => {
const renderFn = mode === 'light' ? render : renderDark;
const { container } = renderFn(<StyledTag hue="blue" />);
const backgroundColor = mode === 'dark' ? PALETTE.blue[600] : PALETTE.blue[700];
const foregroundColor = mode === 'dark' ? PALETTE.grey[1100] : PALETTE.white;

expect(container.firstChild).toHaveStyleRule('background-color', backgroundColor);
expect(container.firstChild).toHaveStyleRule('color', foregroundColor);
});

it.each([['light'], ['dark']])('renders using a "red" hue in %s mode', mode => {
const renderFn = mode === 'light' ? render : renderDark;
const { container } = renderFn(<StyledTag hue="red" />);
const backgroundColor = mode === 'dark' ? PALETTE.red[600] : PALETTE.red[700];
const foregroundColor = mode === 'dark' ? PALETTE.grey[1100] : PALETTE.white;

expect(container.firstChild).toHaveStyleRule('background-color', backgroundColor);
expect(container.firstChild).toHaveStyleRule('color', foregroundColor);
});

it.each([['light'], ['dark']])('renders using a "green" hue in %s mode', mode => {
const renderFn = mode === 'light' ? render : renderDark;
const { container } = renderFn(<StyledTag hue="green" />);
const backgroundColor = mode === 'dark' ? PALETTE.green[600] : PALETTE.green[700];
const foregroundColor = mode === 'dark' ? PALETTE.grey[1100] : PALETTE.white;

expect(container.firstChild).toHaveStyleRule('background-color', backgroundColor);
expect(container.firstChild).toHaveStyleRule('color', foregroundColor);
});

it.each([['light'], ['dark']])('renders using a "yellow" hue in %s mode', mode => {
const renderFn = mode === 'light' ? render : renderDark;
const { container } = renderFn(<StyledTag hue="yellow" />);
const foregroundColor = mode === 'dark' ? PALETTE.grey[1100] : PALETTE.yellow[900];

expect(container.firstChild).toHaveStyleRule('background-color', PALETTE.yellow[400]);
expect(container.firstChild).toHaveStyleRule('color', foregroundColor);
});

it.each([['light'], ['dark']])('renders using a "kale" hue in %s mode', mode => {
const renderFn = mode === 'light' ? render : renderDark;
const { container } = renderFn(<StyledTag hue="kale" />);
const backgroundColor = mode === 'dark' ? PALETTE.kale[500] : PALETTE.kale[800];
const foregroundColor = mode === 'dark' ? PALETTE.grey[1100] : PALETTE.white;

expect(container.firstChild).toHaveStyleRule('background-color', color);
expect(container.firstChild).toHaveStyleRule('background-color', backgroundColor);
expect(container.firstChild).toHaveStyleRule('color', foregroundColor);
});

it('renders using a custom hue', () => {
const { container } = render(<StyledTag hue="azure" />);
const color = getColorV8('azure', 600);
it.each([['light'], ['dark']])('renders using a custom hue for %s mode', mode => {
const renderFn = mode === 'light' ? render : renderDark;
const { container } = renderFn(<StyledTag hue="azure" />);
const backgroundColor = mode === 'dark' ? PALETTE.azure[500] : PALETTE.azure[700];

expect(container.firstChild).toHaveStyleRule('background-color', color);
expect(container.firstChild).toHaveStyleRule('background-color', backgroundColor);
});

it('renders a dark foreground on a light background', () => {
const { container } = render(<StyledTag hue="white" />);
const color = PALETTE.grey[800];

expect(container.firstChild).toHaveStyleRule('color', color);
expect(container.firstChild).toHaveStyleRule('color', PALETTE.grey[1100]);
});

it('renders a light foreground on a dark background', () => {
const { container } = render(<StyledTag hue="black" />);
const color = PALETTE.white;

expect(container.firstChild).toHaveStyleRule('color', color);
expect(container.firstChild).toHaveStyleRule('color', PALETTE.white);
});
});
});
113 changes: 82 additions & 31 deletions packages/tags/src/styled/StyledTag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,64 +9,115 @@ import styled, { css, ThemeProps, DefaultTheme } from 'styled-components';
import { math, readableColor } from 'polished';
import {
DEFAULT_THEME,
getColorV8,
retrieveComponentStyles,
getLineHeight,
SELECTOR_FOCUS_VISIBLE,
focusStyles
focusStyles,
getColor,
IGardenTheme
} from '@zendeskgarden/react-theming';
import { StyledAvatar } from './StyledAvatar';
import { StyledClose } from './StyledClose';
import { ITagProps } from '../types';

const COMPONENT_ID = 'tags.tag_view';

const colorStyles = (props: ITagProps & ThemeProps<DefaultTheme>) => {
/*
* 1. Anchor element reset
* 2. Tags show focus state whether performed by mouse or keyboard
*/
const colorStyles = ({ theme, hue }: ITagProps & ThemeProps<DefaultTheme>) => {
let backgroundColor;
let foregroundColor;
let closeColor;

if (props.hue) {
const shade = props.hue === 'yellow' ? 400 : 600;

backgroundColor = getColorV8(props.hue, shade, props.theme);

if (props.hue === 'yellow' || props.hue === 'lemon') {
foregroundColor = getColorV8('yellow', 800, props.theme);
} else {
foregroundColor = readableColor(
backgroundColor!,
props.theme.palette.grey[800],
props.theme.palette.white as string
);
if (hue) {
foregroundColor = getColor({ theme, variable: 'foreground.onEmphasis' });

switch (hue) {
case 'grey':
case 'neutralHue':
backgroundColor = getColor({
theme,
variable: 'background.emphasis',
dark: { offset: -300 }
});
break;

case 'blue':
case 'primaryHue':
backgroundColor = getColor({ theme, variable: 'background.primaryEmphasis' });
break;

case 'red':
case 'dangerHue':
backgroundColor = getColor({ theme, variable: 'background.dangerEmphasis' });
break;

case 'green':
case 'successHue':
backgroundColor = getColor({ theme, variable: 'background.successEmphasis' });
break;

case 'yellow':
case 'warningHue':
backgroundColor = getColor({ theme, hue: 'warningHue', shade: 400 });

if (theme.colors.base === 'light') {
foregroundColor = getColor({ theme, variable: 'foreground.warningEmphasis' });
}

break;

case 'kale':
case 'chromeHue':
backgroundColor = getColor({
theme,
hue: 'chromeHue',
dark: { shade: 500 },
light: { shade: 800 }
});

break;

default: {
const lightTheme = { ...theme, colors: { ...theme.colors, base: 'light' } } as IGardenTheme;
const darkTheme = { ...theme, colors: { ...theme.colors, base: 'dark' } } as IGardenTheme;
const variable = 'foreground.onEmphasis';

backgroundColor = getColor({ theme, hue });
foregroundColor = readableColor(
backgroundColor,
getColor({ theme: darkTheme, variable }),
getColor({ theme: lightTheme, variable }),
false /* disable strict mode to prevent black */
);

break;
}
}
} else {
backgroundColor = getColorV8('neutralHue', 200, props.theme);
foregroundColor = getColorV8('neutralHue', 700, props.theme);
closeColor = getColorV8('neutralHue', 600, props.theme);
backgroundColor = getColor({
theme,
hue: 'neutralHue',
dark: { shade: 800 },
light: { shade: 200 }
});
foregroundColor = getColor({ theme, variable: 'foreground.default' });
}

return css`
background-color: ${backgroundColor};
color: ${foregroundColor};
&:hover {
color: ${foregroundColor}; /* <a> element reset */
color: ${foregroundColor}; /* [1] */
}
/**
* Tags show their focus state regardless of
* whether it was performed by a mouse or keyboard.
**/
${focusStyles({
theme: props.theme,
theme,
shadowWidth: 'sm',
selector: '&:focus'
selector: '&:focus' /* [2] */
})}
& ${StyledClose} {
color: ${closeColor};
}
`;
};

Expand Down
3 changes: 2 additions & 1 deletion packages/tags/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export interface ITagProps extends HTMLAttributes<HTMLDivElement> {
size?: (typeof SIZE)[number];
/**
* Sets the color of the tag. Refer to
* [PALETTE](/components/palette#palette)
* theming [colors](components/theme-object#colors)
* or [PALETTE](/components/palette#palette)
* for available colors. Accepts any hex value.
*/
hue?: string;
Expand Down

0 comments on commit e3f1533

Please sign in to comment.