Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

feat(theme): updating color palette and scheme #1069

Merged
merged 132 commits into from
May 9, 2019

Conversation

mnajdova
Copy link
Contributor

@mnajdova mnajdova commented Mar 18, 2019

NOTE: these changes are mostly scoped to Teams theme only. The only core Stardust functionality change, is that the colors object is not available anymore in the style functions, the variables should be the only place defining values for the styles.

Colors updates

First of all, let me start with the motivation for starting this work on the colors. We need to deliver the following functionalities in the definition of the styles:

  1. The colors should be safe for using by developers, in a mean that they should be abstracted away from theme switching. This means that if a developer uses some color in the component’s styles, it should work in all three themes out of the box .
  2. We should have structured way of how the colors available in the app can be accessed. This means, that the color name itself should have some meaning, instead of the values that we currently have (like gray04, or primary08) which doesn’t mean anything in the mind of the developer. We are trying to move more towards names like brand foreground color, or brand foreground hover color, that will express better the intent of its usage.
  3. There should be one source of truth for all colors available in the application, so that designers can pick from a predefined set of colors when creating the design tokens, and colors should not be used if they don’t exist in this set (this will move us to consistent and predictable designs)

With these in mind, we will introduce two things for the Light, Dark and High Contrast Teams themes.

  • color palette, which is the set of all colors available in the application, and will not change in any of the themes
  • color schemes, which will define the design tokens for colors trough out all different themes, with the following naming structure: color => area => state. For example, the brand color can be expressed in the following format:
// the colors object used here is basically the color palette that we mention before
{
  foreground: colors.brand[600],
  background: colors.brand[50],
  border: colors.brand[600],
  shadow: colors.brand[600],

  foregroundHover: colors.white,
  backgroundHover: colors.brand[300],
  borderHover: colors.brand[600],
  shadowHover: colors.brand[600],

  foregroundActive: colors.white,
  backgroundActive: colors.brand[500],
  borderActive: colors.brand[600],
  shadowActive: colors.brand[50],

  foregroundFocus: colors.brand[50],
  backgroundFocus: colors.brand[600],
  borderFocus: colors.brand[600],
  shadowFocus: colors.brand[600],

  foregroundFocusWithin: colors.brand[50],
  backgroundFocusWithin: colors.brand[600],
  borderFocusWithin: colors.brand[600],
  shadowFocusWithin: colors.brand[600],

  foregroundDisabled: colors.brand[50],
  backgroundDisabled: colors.brand[600],
  borderDisabled: colors.brand[600],
  shadowDisabled: colors.brand[600],
}

The changes applied in the PR

We introduced one global color palette, created by design, that contains all colors that should be used in the Teams application. This will not be changed across different themes, it will always be the same object.

Apart from this, we created three color schemes, one in each theme, which are identical in their structure (so people can pick values from them safely), but the actual values of the picked token will have different values for colors. For example, this is how the brand part of the color scheme, looks in light, hoc and dark theme:

image
image

What this means is that, if a developer use in their styles use: colorScheme.primary.backgroundHover, it will correctly be mapped to #8DBDE6 in light/dark theme, and to #000 in hoc theme. This is Stardust’s proposal of how the design tokens can be organized, we still have to work with design to conclude on this. As an example of possible change in the future is that, we may introduce inverted schemas for each of the themes.

Fixes #898 and fixes #707

Introducing colors in components.

Let's say that you want to introduce Foo component that is able to color itself with theme being switched. How should you start with this:

  1. Design should specify in the specs which colors should be used for this component, for example:
// variables, default theme
return {
    atMentionMeColor: siteVariables.colorScheme.pink.foreground,
    atMentionOtherColor: siteVariables.colorScheme.default.foreground1,
    importantColor: siteVariables.colorScheme.red.foreground,
   ...
}

This colors will be mapped correctly in all different themes, so the user won't have to do the overrides.

  1. If the colors you need don't map in the color scheme, then take a look on the Design Spec and use colors from siteVariables, as a first thing? Like
// variables, default theme
return {
    atMentionMeColor: siteVariables.colors.pink[500], // those are palette colors
    atMentionOtherColor: siteVariables.colors.grey[600],
    importantColor: siteVariables.colors.red[500]
   ...
}

Then, to override colors for different themes, according to the Design Spec:

// variables, Dark Theme
return {
    atMentionMeColor: siteVariables.colors.red[500], // overriding with palette color
   ...
}
// variables, HC Theme
return {
    atMentionMeColor: siteVariables.accessibleYellow,
   ...
}

On the other hand, if you are developing component that should support the color prop, then you may use directly the color scheme, pick the color that is provided by the prop, and use directly the values from the color scheme (again those should be approved by design for the component being developed).

BREAKING CHANGES:

I will introduce here the mappings for the colors of all the three Teams themes to the corresponding value in the global color palette.

Light theme

export const gray02 = colors.grey[500]
export const gray03 = colors.grey[450]
export const gray04 = colors.grey[350]
export const gray06 = colors.grey[250]
export const gray08 = colors.grey[200]
export const gray09 = colors.grey[150]
export const gray10 = colors.grey[100]
export const gray14 = colors.grey[50]

export const brand04 = colors.brand[800]
export const brand08 = colors.brand[500]

export const magenta = '#B24782' // no mapping color (should we update this to pink[500] - '#B4009E')
export const orchid = colors.pink[600]

export const red = colors.red[400]
export const red08 = colors.red[100] // no mapping color $app-red-08 (updated to '#F3D6D8')
export const red10 = colors.red[50]

Dark theme

export const black = colors.grey[800]

export const gray02 = colors.grey[250]
export const gray03 = colors.grey[300]
export const gray04 = colors.grey[400]
export const gray06 = colors.grey[450]
export const gray08 = colors.grey[500]
export const gray09 = colors.grey[550]
export const gray10 = colors.grey[600] // no mapping color (updated to '#323131')
export const gray14 = colors.grey[700]
export const gray850 = colors.grey[850] // not safe for theme consistency (these colors are not mapped in all themes)
export const gray600 = colors.grey[600] // not safe for theme consistency (these colors are not mapped in all themes)

export const brand = colors.brand[600]
export const brand02 = colors.brand[200]
export const brand04 = colors.brand[300]
export const brand06 = colors.brand[400]
export const brand08 = colors.brand[500]
export const brand12 = colors.brand[600]
export const brand14 = colors.brand[800]
export const brand16 = colors.brand[900]

export const orange04 = colors.orange[300]
export const magenta = '#cf6098' // no mapping color (should we update this to pink[300] - '#DE569A')
export const orchid = '#ae3d84' // no mapping color
export const red = '#d74654' // no mapping color (should we update this to red[300] - '#E73550')
export const red08 = '#4f232b' // no mapping color (should we update this to red[900] - '#1E040A')
export const green04 = colors.green[200]

HC theme

export const black = colors.black // not safe for theme consistency (we should use here some foreground/background color from the color scheme, and mapped that to black in the HC theme)
export const white = colors.white // not safe for theme consistency (we should use here some foreground/background color from the color scheme, and mapped that to white in the HC theme)
export const green04 = colors.green[200]

Previous color palette mappings

primary: { // was renamed to brand
  50: '#F4F4FC', // NO CHANGE
  100: '#E2E2F6', => colors.brand[200]
  200: '#BDBDE6', => colors.brand[300]
  300: '#8F90C1', //  no mapping color - consult design if used
  400: '#6E70AE', //  no mapping color - consult design if used
  500: '#6264A7', => colors.brand[600]
  600: '#55578D', //  no mapping color - consult design if used
  700: '#4A4C78', //  no mapping color - consult design if used
  800: '#414265', //  no mapping color - consult design if used
  900: '#33344A', // NO CHANGE
},
green: {
  50: '#D3E4DB', //  no mapping color - consult design if used
  100: '#C4DCCF', //  no mapping color - consult design if used
  200: '#B3D1C1', //  no mapping color - consult design if used
  300: '#9DC4AF', //  no mapping color - consult design if used
  400: '#8CBAA1', //  no mapping color - consult design if used
  500: '#7BB093', //  no mapping color - consult design if used
  600: '#68A584', //  no mapping color - consult design if used
  700: '#579A75', //  no mapping color - consult design if used
  800: '#458F67', //  no mapping color - consult design if used
  900: '#237B4B', => colors.green[600]
},
grey: {
  50: '#FFFFFF', // NO CHANGE, recommended to use colors.white
  100: '#E6E6E6', //  no mapping color - consult design if used
  200: '#CDCCCC', //  no mapping color - consult design if used
  300: '#B8B8B8', //  no mapping color - consult design if used
  400: '#A2A2A2', //  no mapping color - consult design if used
  500: '#8C8C8C', //  no mapping color - consult design if used
  600: '#747373', //  no mapping color - consult design if used
  700: '#5F5E5E', //  no mapping color - consult design if used
  800: '#404040', //  no mapping color - consult design if used
  900: '#252424', // colors.grey[750] - '#252424' - slightly different color, approved by design
},
orange: {
  50: '#FEF9F7', //  no mapping color - consult design if used
  100: '#FDF0EB', //  no mapping color - consult design if used
  200: '#FAE3DA', //  no mapping color - consult design if used
  300: '#F8D3C5', //  no mapping color - consult design if used
  400: '#F5C6B3', //  no mapping color - consult design if used
  500: '#F4B8A1', //  no mapping color - consult design if used
  600: '#F2AD92', //  no mapping color - consult design if used
  700: '#F0A081', //  no mapping color - consult design if used
  800: '#ED8E6A', //  no mapping color - consult design if used
  900: '#E97548', => colors.orange[300]
},
pink: {
  50: '#E8BDD5', //  no mapping color - consult design if used
  100: '#E1ABC8', //  no mapping color - consult design if used
  200: '#DA9EBF', //  no mapping color - consult design if used
  300: '#D693B8', //  no mapping color - consult design if used
  400: '#D28BB2', //  no mapping color - consult design if used
  500: '#CA7BA6', //  no mapping color - consult design if used
  600: '#C775A3', //  no mapping color - consult design if used
  700: '#C06597', //  no mapping color - consult design if used
  800: '#BA598F', //  no mapping color - consult design if used
  900: '#B34A84', //  no mapping color - consult design if used
},
red: {
  50: '#F2D1D7', //  no mapping color - consult design if used
  100: '#ECBDC5', //  no mapping color - consult design if used
  200: '#E8AFB9', //  no mapping color - consult design if used
  300: '#E39EAA', //  no mapping color - consult design if used
  400: '#DE8D9B', //  no mapping color - consult design if used
  500: '#D97B8C', //  no mapping color - consult design if used
  600: '#D56B7E', //  no mapping color - consult design if used
  700: '#D05B70', //  no mapping color - consult design if used
  800: '#CC4B61', //  no mapping color - consult design if used
  900: '#C4314B', => colors.red[400]
},
yellow: {
  50: '#FEF5D0', //  no mapping color - consult design if used
  100: '#FDF1BE', //  no mapping color - consult design if used
  200: '#FDEEAE', //  no mapping color - consult design if used
  300: '#FBEA9D', //  no mapping color - consult design if used
  400: '#FCE78E', //  no mapping color - consult design if used
  500: '#FAE37C', //  no mapping color - consult design if used
  600: '#FAE06C', //  no mapping color - consult design if used
  700: '#F9DC58', //  no mapping color - consult design if used
  800: '#F9D844', //  no mapping color - consult design if used
  900: '#F8D22A', => colors.yellow[400]
},
darkOrange: {
  50: '#F9ECEA', => colors.orange[50]
  100: '#ECBCB3', //  no mapping color - consult design if used
  200: '#E29C8F', //  no mapping color - consult design if used
  300: '#D97B69', //  no mapping color - consult design if used
  400: '#CC4A31', => colors.orange[400]
  500: '#C5472F', //  no mapping color - consult design if used
  600: '#B7432D', //  no mapping color - consult design if used
  700: '#A73D29', //  no mapping color - consult design if used
  800: '#983927', //  no mapping color - consult design if used
  900: '#833122', => colors.orange[700]
},
lightGreen: {
  50: '#E7F2D9', //  no mapping color - consult design if used
  100: '#DFEECD', //  no mapping color - consult design if used
  200: '#D8EAC1', //  no mapping color - consult design if used
  300: '#CDE3B0', //  no mapping color - consult design if used
  400: '#C6DFA4', //  no mapping color - consult design if used
  500: '#BDDB96', //  no mapping color - consult design if used
  600: '#B4D689', //  no mapping color - consult design if used
  700: '#ACD17B', //  no mapping color - consult design if used
  800: '#A1CC6B', //  no mapping color - consult design if used
  900: '#92C353', => colors.green[200]
},
magenta: {
  50: '#E8D4E0', //  no mapping color - consult design if used
  100: '#DEC0D2', //  no mapping color - consult design if used
  200: '#D4ADC5', //  no mapping color - consult design if used
  300: '#CDA0BC', //  no mapping color - consult design if used
  400: '#C491B0', //  no mapping color - consult design if used
  500: '#BB7FA4', //  no mapping color - consult design if used
  600: '#B26E98', //  no mapping color - consult design if used
  700: '#AA5F8D', //  no mapping color - consult design if used
  800: '#A14F82', //  no mapping color - consult design if used
  900: '#953872', //  no mapping color - consult design if used
},
postOrange: {
  50: '#FDF6F7', //  no mapping color - consult design if used
  100: '#FBF2F3', //  no mapping color - consult design if used
  200: '#FAEEEF', //  no mapping color - consult design if used
  300: '#F9ECED', //  no mapping color - consult design if used
  400: '#F8E9EA', //  no mapping color - consult design if used
  500: '#F7E5E6', //  no mapping color - consult design if used
  600: '#F7E2E4', //  no mapping color - consult design if used
  700: '#F5DEE0', //  no mapping color - consult design if used
  800: '#F5DBDD', //  no mapping color - consult design if used
  900: '#F3D6D8', => colors.red[100]
},

Renamed variables

  • textVariables.ts
  colors => colorScheme
  • dividerVariables.ts
  colors => colorScheme
  • menuVariables.ts
  verticalBorderColor: string - REMOVED, was not used anywhere

  focusedBorder => borderColorFocus
  focusedOutline => outlineColorFocus
  focusedBackgroundColor => backgroundColorFocus

  hoverBackgroundColor => backgroundColorHover

  activeColor => colorActive
  activeBackgroundColor => backgroundColorActive
  iconOnlyActiveColor => iconOnlyColorActive

  disabledColor => colorDisabled
  verticalItemBorder => verticalItemBorderWidth + verticalItemBorderColor

  // these primary related colors are deleted and are now picked from the color scheme
  primaryActiveColor: string
  primaryActiveBackgroundColor: string
  primaryActiveBorderColor: string
  primaryFocusedColor: string
  primaryFocusedBackgroundColor: string
  primaryHoverBorderColor: string
  primaryUnderlinedBorderColor: string

Other breaking changes

  • Every usage of color='primary' should be replace with color='brand'
  • colors object is not available in the style function anymore, the colorScheme object should be used instead.

@codecov
Copy link

codecov bot commented Mar 18, 2019

Codecov Report

Merging #1069 into master will decrease coverage by 0.6%.
The diff coverage is 30.7%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1069      +/-   ##
==========================================
- Coverage   72.85%   72.24%   -0.61%     
==========================================
  Files         747      752       +5     
  Lines        5658     5614      -44     
  Branches     1655     1671      +16     
==========================================
- Hits         4122     4056      -66     
- Misses       1529     1551      +22     
  Partials        7        7
Impacted Files Coverage Δ
...teams/components/Attachment/attachmentVariables.ts 0% <ø> (ø) ⬆️
...s/components/RadioGroup/radioGroupItemVariables.ts 0% <ø> (ø) ⬆️
...eams-dark/components/Reaction/reactionVariables.ts 0% <ø> (ø) ⬆️
packages/react/src/components/Label/Label.tsx 95.23% <ø> (ø) ⬆️
.../src/themes/teams/components/Menu/menuVariables.ts 0% <ø> (ø) ⬆️
.../themes/teams/components/Header/headerVariables.ts 0% <ø> (ø) ⬆️
.../src/themes/teams/components/Icon/iconVariables.ts 0% <ø> (-50%) ⬇️
...src/themes/base/components/Input/inputVariables.ts 0% <ø> (ø) ⬆️
...es/teams/components/Popup/popupContentVariables.ts 0% <ø> (ø) ⬆️
.../themes/teams/components/List/listItemVariables.ts 0% <ø> (ø) ⬆️
... and 74 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 43c90d6...4c6fbc5. Read the comment docs.

backgroundFocus1: siteVars.colors.brand[300],
},
}),
color: siteVars.colors.grey[500],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a plan for future refactoring of the color palette/variables?

I am confused with which variable is used for what and when:

  • colorScheme.foreground is never used, we use variables.color
  • for active primary item background goes from color scheme (colorsScheme.backgroundActive1) and can be overriden by variable (variables.backgroundColorActive), color goes from color scheme (colorScheme.foregroundActive) but cannot be overridden.
  • v.backgroundColorActive || colorScheme.backgroundActive1 and the variable defaults to undefined, but v.underlinedBorderColor || colorScheme.backgroundActive1 and the variable is set?

Copy link
Contributor Author

@mnajdova mnajdova May 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, there is a plan for that, but if we were about to do that now, it would took a long time. We will go component by component, and we will update the styles to use the colorScheme values. Usually, they will be used in the following way:

{
  root: ({variables}) => {
    const colors = getColorScheme(variables.colorScheme, color, primary);
    return {
      ...,
      color: v.color || colors.foregroundColor,
    }
  },
  ...
}

The variables would default to undefined, and we need to come up with a way of how to show it in the docs.

For colors that should never change (even if different color or primary prop is provided), we can have directly value in the variable.


export interface TextVariables {
colors: ColorValues<string>
colorScheme: ColorSchemeMapping
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this common type implies that supported ColorNames (in case if color props is supported by component) should be the same for any component of the app. In other words, if there is pink in color names, it should be supported by all components in corresponding schemes.

Won't it be a problem if, for instance, we would need to support extended range of colors for some particular component? Or this case is not considered intentional, as design spec rules explicitly won't allow this to happen? What would be our options to support this case, if necessary?

I see extendColorScheme method, so I might expect that we will introduce only minimal set of color options for base scheme, and will allow components extend this set of necessary, without affecting others - am I right on that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, related question: currently, for any component that supports color prop, it is possible to introduce the following markup :

<Text color={any_color_from_color_names} />
<Segment ... > // same thing

Wondering of we would like to introduce this much flexibility to the client - as, essentially, only subset of these color variants of Text (or Segment) components is supposed to be used in the app.

Of course, client might omit defining colors, and then default one will be applied on render - but then markup will be misleading.

Could you, please, suggest our plans to handle this situations?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good question. As part of this PR, I just scoped this color value for the prop, to the colors we actually have, and I am defaulting this to the default styles if the color is not in this set. Ideally, we would have color values available defined per component, but this was still not specified by design. After we have these specs, we can update the component styles to handle only the colors they are suppose to. These changes were indeed just to unify the colors, and return default styles if something not expected is provided.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, thanks!

@@ -1,8 +1,7 @@
import { ColorValues } from '../../../types'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you, please, walk me through client's workflow on this. So, I would like to introduce Foo component that is able to color itself with theme being switched. What is the source I need to use colors from?

Am I right that I am supposed to take a look on the Design Spec and use colors from siteVariables, as a first thing? Like

// variables, default theme
return {
    atMentionMeColor: siteVariables.colors.pink[500], // those are palette colors
    atMentionOtherColor: siteVariables.colors.grey[600],
    importantColor: siteVariables.colors.red[500]
   ...
}

Then, to override colors for different themes, I need to override corresponding variables, according to the Design Spec:

// variables, Dark Theme
return {
    atMentionMeColor: siteVariables.colors.red[500], // overriding with palette color
   ...
}

And then, if I would like to support color variants for my component (i.e. color prop), then this is the moment when I need to plug colorScheme, right - otherwise I don't need it?

// variables, default theme
return {
    colorScheme: siteVariables.colorScheme,
    atMentionMeColor: siteVariables.colors.pink[500], // those are palette colors
    atMentionOtherColor: siteVariables.colors.grey[600],
    importantColor: siteVariables.colors.red[500]
   ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if the component doesn't support color, the color scheme defines the colors that should be used across the app. Design should specify in the specs which colors should be used for this component, for example:

// variables, default theme
return {
    atMentionMeColor: siteVariables.colorScheme.pink.foreground, // those are palette colors
    atMentionOtherColor: siteVariables.colorScheme.default.foreground1,
    importantColor: siteVariables.colorScheme.red.foreground,
   ...
}

This colors will be mapped correctly in all different themes, so the user won't have to do the overrides. Currently we have only the default and primary color schemes, but design is working on defining the schemes for the other colors as well.

On the other hand, if the component supports the color prop, you are right, they can leverage further the color scheme, by picking the color provided by the color prop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it - so, essentially, the vector of Design Spec rules reduces to the following:

  • wherever it is possible, use colors from color sheme
  • if rules of color scheme doesn't apply, define color from color palatte

Am I right here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wherever it is possible, use colors from color sheme

Yep, totally right

if rules of color scheme doesn't apply, define color from color palatte

Yep, and override them to the correct values in all different themes.

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

Successfully merging this pull request may close these issues.

Improve the typings of the colors Improving the color mechanism and organization
7 participants