Skip to content

Commit

Permalink
feat(tokens): Add status color tokens (microsoft#28006)
Browse files Browse the repository at this point in the history
* feat(tokens): Add status color tokens

* change files

* bump target in perf-test-react-components to support Object.entries()

* add status tokens to dark and HC

* map danger to cranberry

* add one-off overrides to match the latest design spec
  • Loading branch information
miroslavstastny authored Aug 3, 2023
1 parent 01cc58f commit 141424a
Show file tree
Hide file tree
Showing 17 changed files with 248 additions and 14 deletions.
4 changes: 2 additions & 2 deletions apps/perf-test-react-components/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"target": "es5",
"target": "ES2019",
"outDir": "lib",
"module": "commonjs",
"jsx": "react",
"experimentalDecorators": true,
"preserveConstEnums": true,
"lib": ["ES2015", "DOM"],
"lib": ["ES2019", "DOM"],
"types": ["webpack-env"]
},
"include": ["src"]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "feat(tokens): add status color tokens",
"packageName": "@fluentui/react-theme-sass",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "feat: add status color tokens",
"packageName": "@fluentui/tokens",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,36 @@ $colorPalettePlatinumBorderActive: var(--colorPalettePlatinumBorderActive);
$colorPaletteAnchorBackground2: var(--colorPaletteAnchorBackground2);
$colorPaletteAnchorForeground2: var(--colorPaletteAnchorForeground2);
$colorPaletteAnchorBorderActive: var(--colorPaletteAnchorBorderActive);

$colorStatusSuccessBackground1: var(--colorStatusSuccessBackground1);
$colorStatusSuccessBackground2: var(--colorStatusSuccessBackground2);
$colorStatusSuccessBackground3: var(--colorStatusSuccessBackground3);
$colorStatusSuccessForeground1: var(--colorStatusSuccessForeground1);
$colorStatusSuccessForeground2: var(--colorStatusSuccessForeground2);
$colorStatusSuccessForeground3: var(--colorStatusSuccessForeground3);
$colorStatusSuccessForegroundInverted: var(--colorStatusSuccessForegroundInverted);
$colorStatusSuccessBorderActive: var(--colorStatusSuccessBorderActive);
$colorStatusSuccessBorder1: var(--colorStatusSuccessBorder1);
$colorStatusSuccessBorder2: var(--colorStatusSuccessBorder2);

$colorStatusWarningBackground1: var(--colorStatusWarningBackground1);
$colorStatusWarningBackground2: var(--colorStatusWarningBackground2);
$colorStatusWarningBackground3: var(--colorStatusWarningBackground3);
$colorStatusWarningForeground1: var(--colorStatusWarningForeground1);
$colorStatusWarningForeground2: var(--colorStatusWarningForeground2);
$colorStatusWarningForeground3: var(--colorStatusWarningForeground3);
$colorStatusWarningForegroundInverted: var(--colorStatusWarningForegroundInverted);
$colorStatusWarningBorderActive: var(--colorStatusWarningBorderActive);
$colorStatusWarningBorder1: var(--colorStatusWarningBorder1);
$colorStatusWarningBorder2: var(--colorStatusWarningBorder2);

$colorStatusDangerBackground1: var(--colorStatusDangerBackground1);
$colorStatusDangerBackground2: var(--colorStatusDangerBackground2);
$colorStatusDangerBackground3: var(--colorStatusDangerBackground3);
$colorStatusDangerForeground1: var(--colorStatusDangerForeground1);
$colorStatusDangerForeground2: var(--colorStatusDangerForeground2);
$colorStatusDangerForeground3: var(--colorStatusDangerForeground3);
$colorStatusDangerForegroundInverted: var(--colorStatusDangerForegroundInverted);
$colorStatusDangerBorderActive: var(--colorStatusDangerBorderActive);
$colorStatusDangerBorder1: var(--colorStatusDangerBorder1);
$colorStatusDangerBorder2: var(--colorStatusDangerBorder2);
2 changes: 1 addition & 1 deletion packages/tokens/etc/tokens.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ export const teamsHighContrastTheme: Theme;
export const teamsLightTheme: Theme;

// @public (undocumented)
export type Theme = FontSizeTokens & LineHeightTokens & BorderRadiusTokens & StrokeWidthTokens & HorizontalSpacingTokens & VerticalSpacingTokens & DurationTokens & CurveTokens & ShadowTokens & ShadowBrandTokens & FontFamilyTokens & FontWeightTokens & ColorPaletteTokens & ColorTokens;
export type Theme = FontSizeTokens & LineHeightTokens & BorderRadiusTokens & StrokeWidthTokens & HorizontalSpacingTokens & VerticalSpacingTokens & DurationTokens & CurveTokens & ShadowTokens & ShadowBrandTokens & FontFamilyTokens & FontWeightTokens & ColorPaletteTokens & ColorStatusTokens & ColorTokens;

// @public
export function themeToTokensObject<TTheme extends Theme>(theme: TTheme): Record<keyof TTheme, string>;
Expand Down
35 changes: 33 additions & 2 deletions packages/tokens/src/alias/darkColorPalette.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* color palette used in both darkTheme and teamsDarkTheme */

import { statusSharedColors, personaSharedColors } from '../global/colorPalette';
import { statusSharedColors, personaSharedColors, mappedStatusColors } from '../global/colorPalette';
import { statusSharedColorNames, personaSharedColorNames } from '../sharedColorNames';
import { ColorPaletteTokens, PersonaColorPaletteTokens, StatusColorPaletteTokens } from '../types';
import { ColorPaletteTokens, ColorStatusTokens, PersonaColorPaletteTokens, StatusColorPaletteTokens } from '../types';
import { statusColorMapping } from '../statusColorMapping';

const statusColorPaletteTokens = statusSharedColorNames.reduce((acc, sharedColor) => {
const color = sharedColor.slice(0, 1).toUpperCase() + sharedColor.slice(1);
Expand Down Expand Up @@ -49,3 +50,33 @@ personaColorPaletteTokens.colorPaletteDarkRedBackground2 = personaSharedColors.d
personaColorPaletteTokens.colorPalettePlumBackground2 = personaSharedColors.plum.shade20;

export const colorPaletteTokens: ColorPaletteTokens = { ...statusColorPaletteTokens, ...personaColorPaletteTokens };

export const colorStatusTokens: ColorStatusTokens = Object.entries(statusColorMapping).reduce(
(acc, [statusColor, sharedColor]) => {
const color = statusColor.slice(0, 1).toUpperCase() + statusColor.slice(1);

// TODO: double check the mapping with design - see the one-off patches above
const statusColorTokens = {
[`colorStatus${color}Background1`]: mappedStatusColors[sharedColor].shade40,
[`colorStatus${color}Background2`]: mappedStatusColors[sharedColor].shade30,
[`colorStatus${color}Background3`]: mappedStatusColors[sharedColor].primary,
[`colorStatus${color}Foreground1`]: mappedStatusColors[sharedColor].tint30,
[`colorStatus${color}Foreground2`]: mappedStatusColors[sharedColor].tint40,
[`colorStatus${color}Foreground3`]: mappedStatusColors[sharedColor].tint20,
[`colorStatus${color}BorderActive`]: mappedStatusColors[sharedColor].tint30,
[`colorStatus${color}ForegroundInverted`]: mappedStatusColors[sharedColor].shade10,
[`colorStatus${color}Border1`]: mappedStatusColors[sharedColor].primary,
[`colorStatus${color}Border2`]: mappedStatusColors[sharedColor].tint20,
};

return Object.assign(acc, statusColorTokens);
},
{} as ColorStatusTokens,
);

// one-off overrides for colorStatus tokens
colorStatusTokens.colorStatusDangerForeground3 = mappedStatusColors[statusColorMapping.danger].tint30;
colorStatusTokens.colorStatusDangerBorder2 = mappedStatusColors[statusColorMapping.danger].tint30;
colorStatusTokens.colorStatusSuccessForeground3 = mappedStatusColors[statusColorMapping.success].tint40;
colorStatusTokens.colorStatusSuccessBorder2 = mappedStatusColors[statusColorMapping.success].tint40;
colorStatusTokens.colorStatusWarningForegroundInverted = mappedStatusColors[statusColorMapping.warning].shade20;
26 changes: 25 additions & 1 deletion packages/tokens/src/alias/highContrastColorPalette.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { hcHighlight, hcCanvas, hcCanvasText } from '../global/colors';
import { statusSharedColorNames, personaSharedColorNames } from '../sharedColorNames';
import { ColorPaletteTokens, PersonaColorPaletteTokens, StatusColorPaletteTokens } from '../types';
import { ColorPaletteTokens, ColorStatusTokens, PersonaColorPaletteTokens, StatusColorPaletteTokens } from '../types';
import { statusColorMapping } from '../statusColorMapping';

const statusColorPaletteTokens = statusSharedColorNames.reduce((acc, sharedColor) => {
const color = sharedColor.slice(0, 1).toUpperCase() + sharedColor.slice(1);
Expand Down Expand Up @@ -36,3 +37,26 @@ const personaColorPaletteTokens = personaSharedColorNames.reduce((acc, sharedCol
}, {} as PersonaColorPaletteTokens);

export const colorPaletteTokens: ColorPaletteTokens = { ...statusColorPaletteTokens, ...personaColorPaletteTokens };

export const colorStatusTokens: ColorStatusTokens = Object.entries(statusColorMapping).reduce(
(acc, [statusColor, sharedColor]) => {
const color = statusColor.slice(0, 1).toUpperCase() + statusColor.slice(1);

// TODO: double check the mapping with design
const statusColorTokens = {
[`colorStatus${color}Background1`]: hcCanvas,
[`colorStatus${color}Background2`]: hcCanvas,
[`colorStatus${color}Background3`]: hcCanvasText,
[`colorStatus${color}Foreground1`]: hcCanvasText,
[`colorStatus${color}Foreground2`]: hcCanvasText,
[`colorStatus${color}Foreground3`]: hcCanvasText,
[`colorStatus${color}BorderActive`]: hcHighlight,
[`colorStatus${color}ForegroundInverted`]: hcCanvasText,
[`colorStatus${color}Border1`]: hcCanvasText,
[`colorStatus${color}Border2`]: hcCanvasText,
};

return Object.assign(acc, statusColorTokens);
},
{} as ColorStatusTokens,
);
33 changes: 31 additions & 2 deletions packages/tokens/src/alias/lightColorPalette.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { statusSharedColors, personaSharedColors } from '../global/colorPalette';
import { statusSharedColors, personaSharedColors, mappedStatusColors } from '../global/colorPalette';
import { statusSharedColorNames, personaSharedColorNames } from '../sharedColorNames';
import { ColorPaletteTokens, PersonaColorPaletteTokens, StatusColorPaletteTokens } from '../types';
import { ColorPaletteTokens, ColorStatusTokens, PersonaColorPaletteTokens, StatusColorPaletteTokens } from '../types';
import { statusColorMapping } from '../statusColorMapping';

const statusColorPaletteTokens = statusSharedColorNames.reduce((acc, sharedColor) => {
const color = sharedColor.slice(0, 1).toUpperCase() + sharedColor.slice(1);
Expand Down Expand Up @@ -38,3 +39,31 @@ const personaColorPaletteTokens = personaSharedColorNames.reduce((acc, sharedCol
}, {} as PersonaColorPaletteTokens);

export const colorPaletteTokens: ColorPaletteTokens = { ...statusColorPaletteTokens, ...personaColorPaletteTokens };

export const colorStatusTokens: ColorStatusTokens = Object.entries(statusColorMapping).reduce(
(acc, [statusColor, sharedColor]) => {
const color = statusColor.slice(0, 1).toUpperCase() + statusColor.slice(1);

// TODO: double check the mapping with design
const statusColorTokens = {
[`colorStatus${color}Background1`]: mappedStatusColors[sharedColor].tint60,
[`colorStatus${color}Background2`]: mappedStatusColors[sharedColor].tint40,
[`colorStatus${color}Background3`]: mappedStatusColors[sharedColor].primary,
[`colorStatus${color}Foreground1`]: mappedStatusColors[sharedColor].shade10,
[`colorStatus${color}Foreground2`]: mappedStatusColors[sharedColor].shade30,
[`colorStatus${color}Foreground3`]: mappedStatusColors[sharedColor].primary,
[`colorStatus${color}ForegroundInverted`]: mappedStatusColors[sharedColor].tint30,
[`colorStatus${color}BorderActive`]: mappedStatusColors[sharedColor].primary,
[`colorStatus${color}Border1`]: mappedStatusColors[sharedColor].tint40,
[`colorStatus${color}Border2`]: mappedStatusColors[sharedColor].primary,
};

return Object.assign(acc, statusColorTokens);
},
{} as ColorStatusTokens,
);

// one-off overrides for colorStatus tokens
colorStatusTokens.colorStatusWarningForeground1 = mappedStatusColors[statusColorMapping.warning].shade20;
colorStatusTokens.colorStatusWarningForeground3 = mappedStatusColors[statusColorMapping.warning].shade20;
colorStatusTokens.colorStatusWarningBorder2 = mappedStatusColors[statusColorMapping.warning].shade20;
9 changes: 8 additions & 1 deletion packages/tokens/src/global/colorPalette.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ import {
mink,
platinum,
anchor,
orange,
} from './colors';
import { PersonaSharedColors, StatusSharedColors } from '../types';
import { MappedStatusColors, PersonaSharedColors, StatusSharedColors } from '../types';

export const statusSharedColors: StatusSharedColors = {
red,
Expand Down Expand Up @@ -77,3 +78,9 @@ export const personaSharedColors: PersonaSharedColors = {
platinum,
anchor,
};

export const mappedStatusColors: MappedStatusColors = {
cranberry,
green,
orange,
};
3 changes: 3 additions & 0 deletions packages/tokens/src/sharedColorNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export const personaSharedColorNames = [
'anchor',
] as const;

/* List of global colors which semantic alias status tokens map to */
export const mappedStatusColorNames = ['cranberry', 'green', 'orange'] as const;

/* Names of colors not used in alias tokens but produced by token pipeline as global color tokens. */
export const unusedSharedColorNames = [
'burgundy',
Expand Down
7 changes: 7 additions & 0 deletions packages/tokens/src/statusColorMapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { MappedStatusColorNames } from './types';

export const statusColorMapping: Record<string, MappedStatusColorNames> = {
success: 'green',
warning: 'orange',
danger: 'cranberry',
};
36 changes: 36 additions & 0 deletions packages/tokens/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,42 @@ export const tokens: Record<keyof Theme, string> = {
colorPaletteTealBorderActive: 'var(--colorPaletteTealBorderActive)',
colorPaletteTealForeground2: 'var(--colorPaletteTealForeground2)',

// Color status success tokens
colorStatusSuccessBackground1: 'var(--colorStatusSuccessBackground1)',
colorStatusSuccessBackground2: 'var(--colorStatusSuccessBackground2)',
colorStatusSuccessBackground3: 'var(--colorStatusSuccessBackground3)',
colorStatusSuccessForeground1: 'var(--colorStatusSuccessForeground1)',
colorStatusSuccessForeground2: 'var(--colorStatusSuccessForeground2)',
colorStatusSuccessForeground3: 'var(--colorStatusSuccessForeground3)',
colorStatusSuccessForegroundInverted: 'var(--colorStatusSuccessForegroundInverted)',
colorStatusSuccessBorderActive: 'var(--colorStatusSuccessBorderActive)',
colorStatusSuccessBorder1: 'var(--colorStatusSuccessBorder1)',
colorStatusSuccessBorder2: 'var(--colorStatusSuccessBorder2)',

// Color status warning tokens
colorStatusWarningBackground1: 'var(--colorStatusWarningBackground1)',
colorStatusWarningBackground2: 'var(--colorStatusWarningBackground2)',
colorStatusWarningBackground3: 'var(--colorStatusWarningBackground3)',
colorStatusWarningForeground1: 'var(--colorStatusWarningForeground1)',
colorStatusWarningForeground2: 'var(--colorStatusWarningForeground2)',
colorStatusWarningForeground3: 'var(--colorStatusWarningForeground3)',
colorStatusWarningForegroundInverted: 'var(--colorStatusWarningForegroundInverted)',
colorStatusWarningBorderActive: 'var(--colorStatusWarningBorderActive)',
colorStatusWarningBorder1: 'var(--colorStatusWarningBorder1)',
colorStatusWarningBorder2: 'var(--colorStatusWarningBorder2)',

// Color status danger tokens
colorStatusDangerBackground1: 'var(--colorStatusDangerBackground1)',
colorStatusDangerBackground2: 'var(--colorStatusDangerBackground2)',
colorStatusDangerBackground3: 'var(--colorStatusDangerBackground3)',
colorStatusDangerForeground1: 'var(--colorStatusDangerForeground1)',
colorStatusDangerForeground2: 'var(--colorStatusDangerForeground2)',
colorStatusDangerForeground3: 'var(--colorStatusDangerForeground3)',
colorStatusDangerForegroundInverted: 'var(--colorStatusDangerForegroundInverted)',
colorStatusDangerBorderActive: 'var(--colorStatusDangerBorderActive)',
colorStatusDangerBorder1: 'var(--colorStatusDangerBorder1)',
colorStatusDangerBorder2: 'var(--colorStatusDangerBorder2)',

// Border radius tokens
borderRadiusNone: 'var(--borderRadiusNone)',
borderRadiusSmall: 'var(--borderRadiusSmall)',
Expand Down
48 changes: 47 additions & 1 deletion packages/tokens/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { statusSharedColorNames, personaSharedColorNames, unusedSharedColorNames } from './sharedColorNames';
import {
statusSharedColorNames,
personaSharedColorNames,
unusedSharedColorNames,
mappedStatusColorNames,
} from './sharedColorNames';

/**
* Design tokens for alias colors
Expand Down Expand Up @@ -165,6 +170,42 @@ export type ColorTokens = {
colorBrandShadowKey: string;
};

export type ColorStatusSuccess =
| 'colorStatusSuccessBackground1'
| 'colorStatusSuccessBackground2'
| 'colorStatusSuccessBackground3'
| 'colorStatusSuccessForeground1'
| 'colorStatusSuccessForeground2'
| 'colorStatusSuccessForeground3'
| 'colorStatusSuccessForegroundInverted'
| 'colorStatusSuccessBorderActive'
| 'colorStatusSuccessBorder1'
| 'colorStatusSuccessBorder2';

export type ColorStatusWarning =
| 'colorStatusWarningBackground1'
| 'colorStatusWarningBackground2'
| 'colorStatusWarningBackground3'
| 'colorStatusWarningForeground1'
| 'colorStatusWarningForeground2'
| 'colorStatusWarningForeground3'
| 'colorStatusWarningForegroundInverted'
| 'colorStatusWarningBorderActive'
| 'colorStatusWarningBorder1'
| 'colorStatusWarningBorder2';

export type ColorStatusDanger =
| 'colorStatusDangerBackground1'
| 'colorStatusDangerBackground2'
| 'colorStatusDangerBackground3'
| 'colorStatusDangerForeground1'
| 'colorStatusDangerForeground2'
| 'colorStatusDangerForeground3'
| 'colorStatusDangerForegroundInverted'
| 'colorStatusDangerBorderActive'
| 'colorStatusDangerBorder1'
| 'colorStatusDangerBorder2';

export type ColorPaletteRed =
| 'colorPaletteRedBackground1'
| 'colorPaletteRedBackground2'
Expand Down Expand Up @@ -385,6 +426,8 @@ export type ColorPaletteAnchor =
| 'colorPaletteAnchorForeground2'
| 'colorPaletteAnchorBorderActive';

export type ColorStatusTokens = Record<ColorStatusSuccess | ColorStatusWarning | ColorStatusDanger, string>;

export type StatusColorPaletteTokens = Record<
| ColorPaletteRed
| ColorPaletteGreen
Expand Down Expand Up @@ -454,10 +497,12 @@ export type BrandVariants = Record<Brands, string>;

type StatusSharedColorNames = (typeof statusSharedColorNames)[number];
type PersonaSharedColorNames = (typeof personaSharedColorNames)[number];
export type MappedStatusColorNames = (typeof mappedStatusColorNames)[number];
type UnusedSharedColorNames = (typeof unusedSharedColorNames)[number];

export type StatusSharedColors = Record<StatusSharedColorNames, ColorVariants>;
export type PersonaSharedColors = Record<PersonaSharedColorNames, ColorVariants>;
export type MappedStatusColors = Record<MappedStatusColorNames, ColorVariants>;
export type UnusedSharedColors = Record<UnusedSharedColorNames, ColorVariants>;

export type FontSizeTokens = {
Expand Down Expand Up @@ -717,6 +762,7 @@ export type Theme = FontSizeTokens &
FontFamilyTokens &
FontWeightTokens &
ColorPaletteTokens &
ColorStatusTokens &
ColorTokens;

export type PartialTheme = Partial<Theme>;
3 changes: 2 additions & 1 deletion packages/tokens/src/utils/createDarkTheme.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colorPaletteTokens } from '../alias/darkColorPalette';
import { colorPaletteTokens, colorStatusTokens } from '../alias/darkColorPalette';
import { generateColorTokens } from '../alias/darkColor';

import { borderRadius, fontSizes, lineHeights, fontFamilies, strokeWidths, fontWeights } from '../global/index';
Expand All @@ -25,6 +25,7 @@ export const createDarkTheme: (brand: BrandVariants) => Theme = brand => {

...colorTokens,
...colorPaletteTokens,
...colorStatusTokens,

...createShadowTokens(colorTokens.colorNeutralShadowAmbient, colorTokens.colorNeutralShadowKey),
...createShadowTokens(colorTokens.colorBrandShadowAmbient, colorTokens.colorBrandShadowKey, 'Brand'),
Expand Down
3 changes: 2 additions & 1 deletion packages/tokens/src/utils/createHighContrastTheme.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colorPaletteTokens } from '../alias/highContrastColorPalette';
import { colorPaletteTokens, colorStatusTokens } from '../alias/highContrastColorPalette';
import { generateColorTokens } from '../alias/highContrastColor';

import { borderRadius, fontSizes, lineHeights, fontFamilies, strokeWidths, fontWeights } from '../global/index';
Expand All @@ -25,6 +25,7 @@ export const createHighContrastTheme = (): Theme => {

...colorTokens,
...colorPaletteTokens,
...colorStatusTokens,

...createShadowTokens(colorTokens.colorNeutralShadowAmbient, colorTokens.colorNeutralShadowKey),
...createShadowTokens(colorTokens.colorBrandShadowAmbient, colorTokens.colorBrandShadowKey, 'Brand'),
Expand Down
Loading

0 comments on commit 141424a

Please sign in to comment.