-
Notifications
You must be signed in to change notification settings - Fork 97
feat(typography): new light/dark mode colors (excluding CodeBlock) #1820
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
8bac70c
66c16cc
8a6f3a0
53035b5
888b20e
4bcce5c
6af55b9
a3d6d05
4b3c78e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -6,8 +6,8 @@ | |||
| */ | ||||
|
|
||||
| import React from 'react'; | ||||
| import { DEFAULT_THEME, PALETTE_V8 } from '@zendeskgarden/react-theming'; | ||||
| import { render, renderRtl } from 'garden-test-utils'; | ||||
| import { DEFAULT_THEME, PALETTE } from '@zendeskgarden/react-theming'; | ||||
| import { getRenderFn, render, renderRtl } from 'garden-test-utils'; | ||||
| import { Span } from './Span'; | ||||
| import TestIcon from '@zendeskgarden/svg-icons/src/16/gear-stroke.svg'; | ||||
|
|
||||
|
|
@@ -47,35 +47,47 @@ describe('Span', () => { | |||
| }); | ||||
|
|
||||
| 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(<Span hue={color as any} />); | ||||
|
|
||||
| expect(container.firstChild).toHaveStyleRule('color', (PALETTE_V8 as any)[color][600]); | ||||
| }); | ||||
| const cases = [ | ||||
| 'grey', | ||||
| 'blue', | ||||
| 'kale', | ||||
| 'red', | ||||
| 'green', | ||||
| 'fuschia', | ||||
| 'pink', | ||||
| 'crimson', | ||||
| 'orange', | ||||
| 'lemon', | ||||
| 'lime', | ||||
| 'mint', | ||||
| 'teal', | ||||
| 'azure', | ||||
| 'royal', | ||||
| 'purple', | ||||
| 'yellow' | ||||
| ].reduce<[string, 'light' | 'dark'][]>((arr, hue) => { | ||||
| arr.push([hue, 'light']); | ||||
| arr.push([hue, 'dark']); | ||||
|
|
||||
| return arr; | ||||
| }, []); | ||||
|
|
||||
| it.each(cases)('renders with a "%s" hue in "%s" mode', (hue, mode) => { | ||||
| const { container } = getRenderFn(mode)(<Span hue={hue} />); | ||||
|
|
||||
| expect(container.firstChild).toHaveStyleRule( | ||||
| 'color', | ||||
| (PALETTE as any)[hue][mode === 'light' ? 700 : 500] | ||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Follows
|
||||
| ); | ||||
| }); | ||||
|
|
||||
| it('handles yellow hue with specialized shading', () => { | ||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed since every hue's shade is |
||||
| const { container } = render(<Span hue="yellow" />); | ||||
| it.each<['light' | 'dark', string]>([ | ||||
| ['light', PALETTE.grey[900]], | ||||
| ['dark', PALETTE.grey[300]] | ||||
| ])('renders with a default hue in "%s" mode', (mode, color) => { | ||||
| const { container } = getRenderFn(mode)(<Span />); | ||||
|
|
||||
| expect(container.firstChild).toHaveStyleRule('color', PALETTE_V8.yellow[700]); | ||||
| expect(container.firstChild).toHaveStyleRule('color', color); | ||||
| }); | ||||
| }); | ||||
|
|
||||
|
|
||||
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,17 +6,40 @@ | |||||||||||||||||||||
| */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import styled, { css, DefaultTheme, ThemeProps } from 'styled-components'; | ||||||||||||||||||||||
| import { DEFAULT_THEME, getColorV8, retrieveComponentStyles } from '@zendeskgarden/react-theming'; | ||||||||||||||||||||||
| import { DEFAULT_THEME, getColor, retrieveComponentStyles } from '@zendeskgarden/react-theming'; | ||||||||||||||||||||||
| import { StyledFont, IStyledFontProps } from './StyledFont'; | ||||||||||||||||||||||
| import { ICodeProps } from '../types'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const COMPONENT_ID = 'typography.code'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const colorStyles = (props: IStyledCodeProps & ThemeProps<DefaultTheme>) => { | ||||||||||||||||||||||
| const hue = props.hue || 'neutralHue'; | ||||||||||||||||||||||
| const backgroundColor = getColorV8(hue, 200, props.theme); | ||||||||||||||||||||||
| const shade = hue === 'yellow' ? 800 : 700; | ||||||||||||||||||||||
| const foregroundColor = getColorV8(hue, shade, props.theme); | ||||||||||||||||||||||
| const colorStyles = ({ hue, theme }: IStyledCodeProps & ThemeProps<DefaultTheme>) => { | ||||||||||||||||||||||
| const bgColorArgs: Parameters<typeof getColor>[0] = { theme }; | ||||||||||||||||||||||
| const fgColorArgs: Parameters<typeof getColor>[0] = { theme }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| switch (hue) { | ||||||||||||||||||||||
| case 'green': | ||||||||||||||||||||||
| bgColorArgs.variable = 'background.success'; | ||||||||||||||||||||||
| fgColorArgs.variable = 'foreground.successEmphasis'; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'red': | ||||||||||||||||||||||
| bgColorArgs.variable = 'background.danger'; | ||||||||||||||||||||||
| fgColorArgs.variable = 'foreground.dangerEmphasis'; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'yellow': | ||||||||||||||||||||||
| bgColorArgs.variable = 'background.warning'; | ||||||||||||||||||||||
| fgColorArgs.variable = 'foreground.warningEmphasis'; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| // includes grey | ||||||||||||||||||||||
| default: | ||||||||||||||||||||||
| fgColorArgs.variable = 'foreground.default'; | ||||||||||||||||||||||
| bgColorArgs.hue = 'neutralHue'; | ||||||||||||||||||||||
| bgColorArgs.dark = { shade: 900 }; | ||||||||||||||||||||||
| bgColorArgs.light = { shade: 200 }; | ||||||||||||||||||||||
|
||||||||||||||||||||||
| bgColorArgs.hue = 'neutralHue'; | |
| bgColorArgs.dark = { shade: 900 }; | |
| bgColorArgs.light = { shade: 200 }; | |
| bgColorArgs.variable = 'background.subtle'; | |
| bgColorArgs.dark = { offset: -100 }; | |
| bgColorArgs.light = { offset: 100 }; |
Always better to tie into a variable whenever possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Design opted for a custom component variable for this one:
WIP_code-bg-default
#E8EAEC
I chose to explicitly build the color to reflect their choice and highlight the lack for variable for this hue / shade combo.
I think modifying an existing variable can lead to side-effects and increased maintenance overhead.
For ex, if Design were to change the shade of background.subtle, ENG would have to review all uses of this variable to ensure the resulting shade from offsets is still the desired one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have guidance from design to use getColor's offset feature when possible. They wouldn't be using WIP variables if Figma provided similar functionality. Let's double check the semantics with them. As far as any variables change goes, we would have to review all uses – so I don't think that's a major issue here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following our convo, @jzempel and I decided it was best to leave as-is to better handle future themes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's be sure to double-check intent with design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsupported hues default to grey. If a hue isn't recognized, getColor throws.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This move unintentionally breaks the Code hue API. The expectation is that hue can receive any value that getColor's hue argument can receive. So that includes all Garden hue values (lime, fuschia, etc), hex, etc. So a few things need to happen here:
- continue to give special treatment to Garden's primary colors
- get feedback from design on what shades to fallback to (perhaps dark:900 & light: 200)
- allow
getColorto do it's thing – including throwing an error if someone provides a bogus value
Please see StyledTag for comparison.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StyledCode is "loosely" typed with hue?: string
react-components/packages/typography/src/styled/StyledCode.ts
Lines 31 to 33 in cffdb06
| interface IStyledCodeProps extends Omit<IStyledFontProps, 'size'> { | |
| hue?: string; | |
| size?: ICodeProps['size']; |
In our case, only Code uses this component and restricts possible hue values to only grey, red, green, and yellow.
| export const HUE = ['grey', 'red', 'green', 'yellow'] as const; |
react-components/packages/typography/src/types/index.ts
Lines 84 to 89 in 327ea52
| export interface ICodeProps extends HTMLAttributes<HTMLElement> { | |
| /** Applies color to the background and the text */ | |
| hue?: (typeof HUE)[number]; | |
| /** Adjusts the font size. By default font size is inherited from the surrounding text. */ | |
| size?: (typeof INHERIT_SIZE)[number]; | |
| } |
Therefore, I don't think it's a breaking change, but rather a call to improve clarity by redefining the hue type in StyledCode to match its real-life usage. I must emphasize this suggestion only make sense because StyledCode is exclusively used by Code.
interface IStyledCodeProps extends Omit<IStyledFontProps, 'size'> {
hue?: (typeof HUE)[number];
size?: ICodeProps['size'];
}Toughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, interesting – I was looking at the wrong (Styled) API and testing on v8 with usage like <Code hue="fuschia">. In this case, restricting the hue color values makes sense. I still think allowing getColor to throw on invalid usage – as it now does in other v9 components – is appropriate and better than protecting the consumer from themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hue?: string; was causing confusion. So I refined the type to emphasize that Code, StyledCode's only consumer, strictly supports grey, red, green, yellow, and undefined.
| export const HUE = ['grey', 'red', 'green', 'yellow'] as const; |
Passing an unsupported hue would trigger a TS error or an invalid PropType error at runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isMonospace: true is required by StyledFont to apply the correct styles.
Defining it as a defaultProp didn't make that obvious.
This pattern aligns with other components. For ex:
react-components/packages/buttons/src/styled/StyledAnchor.ts
Lines 18 to 25 in cffdb06
| export const StyledAnchor = styled(StyledButton).attrs<IAnchorProps>(props => ({ | |
| 'data-garden-id': COMPONENT_ID, | |
| 'data-garden-version': PACKAGE_VERSION, | |
| as: 'a', | |
| dir: props.theme.rtl ? 'rtl' : undefined, | |
| isLink: true, | |
| type: undefined | |
| }))` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved specs to
StyledCode.spec.tsx.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine for now. However, the prevailing recent thinking is to test at the element component level rather than at the view component. This way we're penetrating all points of interest. See the newer
ComboboxandMenucomponents in thedropdownspackage for comparison.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for clarifying. I wasn't sure about that. I'll move the specs back to this file instead.