diff --git a/.storybook/preview.js b/.storybook/preview.js index 40f87c5e..aa2ffa9e 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,3 +1,5 @@ +import '../src/css/design-tokens.css'; + export const parameters = { actions: { argTypesRegex: '^on[A-Z].*' }, controls: { diff --git a/docs/Shadows.mdx b/docs/Shadows.mdx new file mode 100644 index 00000000..3c87ce47 --- /dev/null +++ b/docs/Shadows.mdx @@ -0,0 +1,79 @@ +import { Meta, Canvas, Story } from '@storybook/addon-docs'; +import LinkTo from '@storybook/addon-links/react'; + + + +# Shadows + +Shadows convey elevation of elements on a surface + +## Size + +There are 4 different sizes of shadow in MetaMask. + + + + + +| Size | JS | CSS | +| ------ | ----------------- | ----------------------- | +| **XS** | `shadows.size.xs` | `var(--shadow-size-xs)` | +| **SM** | `shadows.size.sm` | `var(--shadow-size-sm)` | +| **MD** | `shadows.size.md` | `var(--shadow-size-md)` | +| **LG** | `shadows.size.lg` | `var(--shadow-size-lg)` | + +## Color + +As well as the neutral colors for shadow 2 other colors exist that are used for the primary and danger button hover states + + + + + +| Color | JS | CSS | +| ----------- | ----------------------- | ----------------------------- | +| **neutral** | `colors.shadow.default` | `var(--color-shadow-default)` | +| **primary** | `colors.primary.shadow` | `var(--color-primary-shadow)` | +| **danger** | `colors.error.shadow` | `var(--color-error-shadow)` | + +## Usage + +Using both size and color tokens, different shadows can be applied to components. + + + + + +| Component | JS | CSS | +| ------------------------ | ------------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| **Card** | `cardShadow: {...shadows.size.xs }` | `box-shadow: var(--shadow-size-xs) var(--color-shadow-default);` | +| **Dropdown** | `dropdownShadow: { ...shadows.size.sm }` | `box-shadow: var(--shadow-size-sm) var(--color-shadow-default);` | +| **Toast** | `toastShadow: { ...shadows.size.md }` | `box-shadow: var(--shadow-size-md) var(--color-shadow-default);` | +| **Modal** | `modalShadow: { ...shadows.size.lg }` | `box-shadow: var(--shadow-size-lg) var(--color-shadow-default);` | +| **Button Primary Hover** | `buttonPrimaryHover: { ...shadows.size.sm, shadowColor: colors.primary.shadow}` | `box-shadow: var(--shadow-size-sm) var(--color-primary-shadow);` | +| **Button Danger Hover** | `buttonDangerHover: { ...shadows.size.sm, shadowColor: colors.primary.shadow}` | `box-shadow: var(--shadow-size-sm) var(--color-error-shadow);` | + +**NOTE: The CSS-in-JS `shadows.size` objects for React Native contain all the correct tokens for neutral shadows. For primary and error shadows change the `shadowColor` key** + +Example shape of the `xs` shadow size object from `shadows` + +``` +size: { + xs: { + shadowColor: colors.light.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 4, + }, + ... +} + +``` + +## References + +- [Figma Light Theme Colors Library(Shadows Page)](https://www.figma.com/file/kdFzEC7xzSNw7cXteqgzDW/%5BColor%5D-Light-Theme?node-id=753%3A719)(internal use only) +- [Figma Dark Theme Colors Library(Shadows Page)](https://www.figma.com/file/rLKsoqpjyoKauYnFDcBIbO/%5BColor%5D-Dark-Theme?node-id=522%3A1022)(internal use only) diff --git a/docs/Shadows.stories.tsx b/docs/Shadows.stories.tsx new file mode 100644 index 00000000..930ee701 --- /dev/null +++ b/docs/Shadows.stories.tsx @@ -0,0 +1,206 @@ +import React from 'react'; +import { Text } from './components'; + +import README from './Shadows.mdx'; + +export default { + title: 'Shadows/Shadows', + parameters: { + docs: { + page: README, + }, + }, +}; + +interface ShadowSwatchProps { + children: any; + style?: object; +} + +const ShadowSwatch = ({ children, style }: ShadowSwatchProps) => ( +
+ {children} +
+); + +export const Size = () => { + return ( +
+ + + XS + + + + + SM + + + + + MD + + + + + LG + + +
+ ); +}; + +export const Color = () => { + return ( +
+ + + Neutral + + + + + Primary + + + + + Danger + + +
+ ); +}; + +export const Usage = () => { + return ( +
+
+ + + Card + + + + + Dropdown + + + + + Toast + + + + + Modal + + +
+
+ + + Button Primary Hover + + + + + Button Danger Hover + + +
+
+ ); +}; diff --git a/docs/components/Text/Text.tsx b/docs/components/Text/Text.tsx index 49eca278..74cedf1d 100644 --- a/docs/components/Text/Text.tsx +++ b/docs/components/Text/Text.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { fontFamilies } from '../../../src/js/typography/fontFamilies'; interface Props { /** @@ -24,5 +25,9 @@ export const Text = ({ as, }: TextProps) => { const Component = as || 'span'; - return {children}; + return ( + + {children} + + ); }; diff --git a/src/css/design-tokens.css b/src/css/design-tokens.css index 2bad19b9..eb7053da 100644 --- a/src/css/design-tokens.css +++ b/src/css/design-tokens.css @@ -97,31 +97,26 @@ --letter-spacing-0: 0; --letter-spacing-1: 2.5%; /* typography scale small screen */ - --typography-s-display-md-font-family: var(--font-family-euclid-circular-b); --typography-s-display-md-font-size: var(--font-size-7); --typography-s-display-md-line-height: var(--line-height-4); --typography-s-display-md-font-weight: var(--font-weight-medium); --typography-s-display-md-letter-spacing: var(--letter-spacing-0); - --typography-s-heading-lg-font-family: var(--font-family-euclid-circular-b); --typography-s-heading-lg-font-size: var(--font-size-6); --typography-s-heading-lg-line-height: var(--line-height-5); --typography-s-heading-lg-font-weight: var(--font-weight-bold); --typography-s-heading-lg-letter-spacing: var(--letter-spacing-0); - --typography-s-heading-md-font-family: var(--font-family-euclid-circular-b); --typography-s-heading-md-font-size: var(--font-size-5); --typography-s-heading-md-line-height: var(--line-height-5); --typography-s-heading-md-font-weight: var(--font-weight-bold); --typography-s-heading-md-letter-spacing: var(--letter-spacing-0); - --typography-s-heading-sm-font-family: var(--font-family-euclid-circular-b); --typography-s-heading-sm-font-size: var(--font-size-4); --typography-s-heading-sm-line-height: var(--line-height-6); --typography-s-heading-sm-font-weight: var(--font-weight-regular); --typography-s-heading-sm-letter-spacing: var(--letter-spacing-0); - --typography-s-heading-sm-regular-font-family: var( --font-family-euclid-circular-b ); @@ -129,31 +124,26 @@ --typography-s-heading-sm-regular-line-height: var(--line-height-6); --typography-s-heading-sm-regular-font-weight: var(--font-weight-regular); --typography-s-heading-sm-regular-letter-spacing: var(--letter-spacing-0); - --typography-s-body-md-bold-font-family: var(--font-family-euclid-circular-b); --typography-s-body-md-bold-font-size: var(--font-size-3); --typography-s-body-md-bold-line-height: var(--line-height-6); --typography-s-body-md-bold-font-weight: var(--font-weight-bold); --typography-s-body-md-bold-letter-spacing: var(--letter-spacing-0); - --typography-s-body-md-font-family: var(--font-family-euclid-circular-b); --typography-s-body-md-font-size: var(--font-size-3); --typography-s-body-md-line-height: var(--line-height-6); --typography-s-body-md-font-weight: var(--font-weight-regular); --typography-s-body-md-letter-spacing: var(--letter-spacing-0); - --typography-s-body-sm-bold-font-family: var(--font-family-euclid-circular-b); --typography-s-body-sm-bold-font-size: var(--font-size-2); --typography-s-body-sm-bold-line-height: var(--line-height-8); --typography-s-body-sm-bold-font-weight: var(--font-weight-bold); --typography-s-body-sm-bold-letter-spacing: var(--letter-spacing-0); - --typography-s-body-sm-font-family: var(--font-family-euclid-circular-b); --typography-s-body-sm-font-size: var(--font-size-2); --typography-s-body-sm-line-height: var(--line-height-8); --typography-s-body-sm-font-weight: var(--font-weight-regular); --typography-s-body-sm-letter-spacing: var(--letter-spacing-0); - --typography-s-body-xs-font-family: var(--font-family-euclid-circular-b); --typography-s-body-xs-font-size: var(--font-size-1); --typography-s-body-xs-line-height: var(--line-height-7); @@ -161,31 +151,26 @@ --typography-s-body-xs-letter-spacing: var(--letter-spacing-0); /* typography scale large screen */ - --typography-l-display-md-font-family: var(--font-family-euclid-circular-b); --typography-l-display-md-font-size: var(--font-size-8); --typography-l-display-md-line-height: var(--line-height-2); --typography-l-display-md-font-weight: var(--font-weight-medium); --typography-l-display-md-letter-spacing: var(--letter-spacing-0); - --typography-l-heading-lg-font-family: var(--font-family-euclid-circular-b); --typography-l-heading-lg-font-size: var(--font-size-7); --typography-l-heading-lg-line-height: var(--line-height-4); --typography-l-heading-lg-font-weight: var(--font-weight-bold); --typography-l-heading-lg-letter-spacing: var(--letter-spacing-0); - --typography-l-heading-md-font-family: var(--font-family-euclid-circular-b); --typography-l-heading-md-font-size: var(--font-size-6); --typography-l-heading-md-line-height: var(--line-height-5); --typography-l-heading-md-font-weight: var(--font-weight-bold); --typography-l-heading-md-letter-spacing: var(--letter-spacing-0); - --typography-l-heading-sm-font-family: var(--font-family-euclid-circular-b); --typography-l-heading-sm-font-size: var(--font-size-5); --typography-l-heading-sm-line-height: var(--line-height-5); --typography-l-heading-sm-font-weight: var(--font-weight-bold); --typography-l-heading-sm-letter-spacing: var(--letter-spacing-0); - --typography-l-heading-sm-regular-font-family: var( --font-family-euclid-circular-b ); @@ -193,36 +178,36 @@ --typography-l-heading-sm-regular-line-height: var(--line-height-5); --typography-l-heading-sm-regular-font-weight: var(--font-weight-bold); --typography-l-heading-sm-regular-letter-spacing: var(--letter-spacing-0); - --typography-l-body-md-bold-font-family: var(--font-family-euclid-circular-b); --typography-l-body-md-bold-font-size: var(--font-size-4); --typography-l-body-md-bold-line-height: var(--line-height-6); --typography-l-body-md-bold-font-weight: var(--font-weight-bold); --typography-l-body-md-bold-letter-spacing: var(--letter-spacing-0); - --typography-l-body-md-font-family: var(--font-family-euclid-circular-b); --typography-l-body-md-font-size: var(--font-size-4); --typography-l-body-md-line-height: var(--line-height-6); --typography-l-body-md-font-weight: var(--font-weight-regular); --typography-l-body-md-letter-spacing: var(--letter-spacing-0); - --typography-l-body-sm-bold-font-family: var(--font-family-euclid-circular-b); --typography-l-body-sm-bold-font-size: var(--font-size-3); --typography-l-body-sm-bold-line-height: var(--line-height-6); --typography-l-body-sm-bold-font-weight: var(--font-weight-bold); --typography-l-body-sm-bold-letter-spacing: var(--letter-spacing-0); - --typography-l-body-sm-font-family: var(--font-family-euclid-circular-b); --typography-l-body-sm-font-size: var(--font-size-3); --typography-l-body-sm-line-height: var(--line-height-6); --typography-l-body-sm-font-weight: var(--font-weight-regular); --typography-l-body-sm-letter-spacing: var(--letter-spacing-0); - --typography-l-body-xs-font-family: var(--font-family-euclid-circular-b); --typography-l-body-xs-font-size: var(--font-size-2); --typography-l-body-xs-line-height: var(--line-height-7); --typography-l-body-xs-font-weight: var(--font-weight-regular); --typography-l-body-xs-letter-spacing: var(--letter-spacing-0); + /* shadow */ + --shadow-size-xs: 0 2px 4px 0; + --shadow-size-sm: 0 2px 8px 0; + --shadow-size-md: 0 2px 16px 0; + --shadow-size-lg: 0 2px 40px 0; } /* @@ -245,11 +230,13 @@ --color-overlay-default: #00000099; --color-overlay-alternative: #000000cc; --color-overlay-inverse: var(--brand-colors-white-white010); + --color-shadow-default: #0000001a; --color-primary-default: var(--brand-colors-blue-blue500); --color-primary-alternative: var(--brand-colors-blue-blue600); --color-primary-muted: #037dd619; --color-primary-inverse: var(--brand-colors-white-white010); --color-primary-disabled: #037dd680; + --color-primary-shadow: #037dd633; --color-secondary-default: var(--brand-colors-orange-orange500); --color-secondary-alternative: var(--brand-colors-orange-orange600); --color-secondary-muted: #f66a0a19; @@ -260,6 +247,7 @@ --color-error-muted: #d73a4919; --color-error-inverse: var(--brand-colors-white-white010); --color-error-disabled: #d73a4980; + --color-error-shadow: #d73a4966; --color-warning-default: var(--brand-colors-orange-orange500); --color-warning-alternative: var(--brand-colors-yellow-yellow600); --color-warning-muted: #ffd33d19; @@ -275,6 +263,13 @@ --color-info-muted: #037dd619; --color-info-inverse: var(--brand-colors-white-white010); --color-info-disabled: #037dd680; + + /* Components */ + /* button */ + --component-button-primary-shadow: var(--shadow-size-sm) + var(--color-primary-shadow); + --component-button-danger-shadow: var(--shadow-size-sm) + var(--color-error-shadow); } /** @@ -295,12 +290,14 @@ --color-border-muted: var(--brand-colors-grey-grey700); --color-overlay-default: #00000099; --color-overlay-alternative: #000000cc; + --color-shadow-default: #00000080; --color-overlay-inverse: var(--brand-colors-white-white010); --color-primary-default: var(--brand-colors-blue-blue400); --color-primary-alternative: var(--brand-colors-blue-blue300); --color-primary-muted: #1098fc26; --color-primary-inverse: var(--brand-colors-white-white010); --color-primary-disabled: #1098fc80; + --color-primary-shadow: #037dd633; --color-secondary-default: var(--brand-colors-orange-orange400); --color-secondary-alternative: var(--brand-colors-orange-orange300); --color-secondary-muted: #f8883b26; @@ -311,6 +308,7 @@ --color-error-muted: #d73a4926; --color-error-inverse: var(--brand-colors-white-white010); --color-error-disabled: #d73a4980; + --color-error-shadow: #d73a4966; --color-warning-default: var(--brand-colors-yellow-yellow500); --color-warning-alternative: var(--brand-colors-yellow-yellow400); --color-warning-muted: #ffd33d26; diff --git a/src/figma/tokens.json b/src/figma/tokens.json index 9512cc77..ef71d96d 100644 --- a/src/figma/tokens.json +++ b/src/figma/tokens.json @@ -935,6 +935,74 @@ "type": "color" } } + }, + "shadows": { + "xs": { + "value": { + "color": "#0000001a", + "type": "dropShadow", + "x": 0, + "y": 2, + "blur": 4, + "spread": 0 + }, + "type": "boxShadow" + }, + "sm": { + "value": { + "color": "#0000001a", + "type": "dropShadow", + "x": 0, + "y": 2, + "blur": 8, + "spread": 0 + }, + "type": "boxShadow" + }, + "md": { + "value": { + "color": "#0000001a", + "type": "dropShadow", + "x": 0, + "y": 2, + "blur": 16, + "spread": 0 + }, + "type": "boxShadow" + }, + "lg": { + "value": { + "color": "#0000001a", + "type": "dropShadow", + "x": 0, + "y": 8, + "blur": 40, + "spread": 0 + }, + "type": "boxShadow" + }, + "primary": { + "value": { + "color": "#037dd633", + "type": "dropShadow", + "x": 0, + "y": 7, + "blur": 42, + "spread": 0 + }, + "type": "boxShadow" + }, + "error": { + "value": { + "color": "#d73a4966", + "type": "dropShadow", + "x": 0, + "y": 7, + "blur": 42, + "spread": 0 + }, + "type": "boxShadow" + } } }, "dark": { @@ -1176,6 +1244,74 @@ "type": "color" } } + }, + "shadows": { + "xs": { + "value": { + "color": "#00000066", + "type": "dropShadow", + "x": 0, + "y": 2, + "blur": 4, + "spread": 0 + }, + "type": "boxShadow" + }, + "sm": { + "value": { + "color": "#00000066", + "type": "dropShadow", + "x": 0, + "y": 2, + "blur": 8, + "spread": 0 + }, + "type": "boxShadow" + }, + "md": { + "value": { + "color": "#00000066", + "type": "dropShadow", + "x": 0, + "y": 2, + "blur": 16, + "spread": 0 + }, + "type": "boxShadow" + }, + "lg": { + "value": { + "color": "#00000066", + "type": "dropShadow", + "x": 0, + "y": 8, + "blur": 40, + "spread": 0 + }, + "type": "boxShadow" + }, + "primary": { + "value": { + "color": "#1098FC66", + "type": "dropShadow", + "x": 0, + "y": 7, + "blur": 42, + "spread": 0 + }, + "type": "boxShadow" + }, + "error": { + "value": { + "color": "#D73A4966", + "type": "dropShadow", + "x": 0, + "y": 7, + "blur": 42, + "spread": 0 + }, + "type": "boxShadow" + } } }, "$themes": [] diff --git a/src/js/colors/colors.test.ts b/src/js/colors/colors.test.ts index 467951c6..a60dfcec 100644 --- a/src/js/colors/colors.test.ts +++ b/src/js/colors/colors.test.ts @@ -76,6 +76,24 @@ describe('Light Theme Colors', () => { ); }); + it('js tokens for shadow.default matches figma tokens light.shadows.xs.value.color', () => { + expect(importableColors.light.shadow.default).toStrictEqual( + designTokens.light.shadows.xs.value.color, + ); + + expect(importableColors.light.shadow.default).toStrictEqual( + designTokens.light.shadows.sm.value.color, + ); + + expect(importableColors.light.shadow.default).toStrictEqual( + designTokens.light.shadows.md.value.color, + ); + + expect(importableColors.light.shadow.default).toStrictEqual( + designTokens.light.shadows.lg.value.color, + ); + }); + it('js tokens for overlay.inverse matches figma tokens overlay.inverse', () => { expect(importableColors.light.overlay.inverse).toStrictEqual( designTokens.light.colors.overlay.inverse.value, @@ -106,6 +124,12 @@ describe('Light Theme Colors', () => { ); }); + it('js tokens for primary.shadow matches figma tokens shadows.primary', () => { + expect(importableColors.light.primary.shadow).toStrictEqual( + designTokens.light.shadows.primary.value.color, + ); + }); + it('js tokens for secondary.default matches figma tokens secondary.default', () => { expect(importableColors.light.secondary.default).toStrictEqual( designTokens.light.colors.secondary.default.value, @@ -154,6 +178,12 @@ describe('Light Theme Colors', () => { ); }); + it('js tokens for error.shadow matches figma tokens shadows.error', () => { + expect(importableColors.light.error.shadow).toStrictEqual( + designTokens.light.shadows.error.value.color, + ); + }); + it('js tokens for warning.default matches figma tokens warning.default', () => { expect(importableColors.light.warning.default).toStrictEqual( designTokens.light.colors.warning.default.value, @@ -306,6 +336,24 @@ describe('Dark Theme Colors', () => { ); }); + it('js tokens for shadow.default matches figma tokens dark.shadows.xs.value.color', () => { + expect(importableColors.dark.shadow.default).toStrictEqual( + designTokens.dark.shadows.xs.value.color, + ); + + expect(importableColors.dark.shadow.default).toStrictEqual( + designTokens.dark.shadows.sm.value.color, + ); + + expect(importableColors.dark.shadow.default).toStrictEqual( + designTokens.dark.shadows.md.value.color, + ); + + expect(importableColors.dark.shadow.default).toStrictEqual( + designTokens.dark.shadows.lg.value.color, + ); + }); + it('js tokens for primary.default matches figma tokens primary.default', () => { expect(importableColors.dark.primary.default).toStrictEqual( designTokens.dark.colors.primary.default.value, @@ -330,6 +378,12 @@ describe('Dark Theme Colors', () => { ); }); + it('js tokens for primary.shadow matches figma tokens shadows.primary', () => { + expect(importableColors.dark.primary.shadow).toStrictEqual( + designTokens.dark.shadows.primary.value.color, + ); + }); + it('js tokens for secondary.default matches figma tokens secondary.default', () => { expect(importableColors.dark.secondary.default).toStrictEqual( designTokens.dark.colors.secondary.default.value, @@ -378,6 +432,12 @@ describe('Dark Theme Colors', () => { ); }); + it('js tokens for error.shadow matches figma tokens shadows.error', () => { + expect(importableColors.dark.error.shadow).toStrictEqual( + designTokens.dark.shadows.error.value.color, + ); + }); + it('js tokens for warning.default matches figma tokens warning.default', () => { expect(importableColors.dark.warning.default).toStrictEqual( designTokens.dark.colors.warning.default.value, diff --git a/src/js/index.ts b/src/js/index.ts index 5fa681c3..cccb7b0f 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -5,3 +5,4 @@ export { darkTheme } from './themes'; // DEPRECATED in favor of importing theme objects above export { colors } from './colors'; export { typography } from './typography'; +export { shadows } from './shadows'; diff --git a/src/js/themes/darkTheme/colors.ts b/src/js/themes/darkTheme/colors.ts index aa73a5b9..659d1693 100644 --- a/src/js/themes/darkTheme/colors.ts +++ b/src/js/themes/darkTheme/colors.ts @@ -28,12 +28,16 @@ export const colors: ThemeColors = { inverse: '#FCFCFC', alternative: '#000000CC', }, + shadow: { + default: '#00000066', + }, primary: { default: '#1098FC', alternative: '#43AEFC', muted: '#1098FC26', inverse: '#FCFCFC', disabled: '#1098FC80', + shadow: '#1098FC66', }, secondary: { default: '#F8883B', @@ -48,6 +52,7 @@ export const colors: ThemeColors = { muted: '#D73A4926', inverse: '#FCFCFC', disabled: '#D73A4980', + shadow: '#D73A4966', }, warning: { default: '#FFD33D', diff --git a/src/js/themes/darkTheme/darkTheme.test.ts b/src/js/themes/darkTheme/darkTheme.test.ts index 36ef1a57..07122c0f 100644 --- a/src/js/themes/darkTheme/darkTheme.test.ts +++ b/src/js/themes/darkTheme/darkTheme.test.ts @@ -15,4 +15,16 @@ describe('dark Theme', () => { designTokens.global.fontFamilies['euclid-circular-b'].value, ); }); + + it('shadow tokens are exported from darkTheme by checking first shadow size object', () => { + expect(darkTheme.shadows.size.xs).toStrictEqual({ + shadowColor: designTokens.dark.shadows.xs.value.color, + shadowOffset: { + width: designTokens.dark.shadows.xs.value.x, + height: designTokens.dark.shadows.xs.value.y, + }, + shadowOpacity: 1, + shadowRadius: designTokens.dark.shadows.xs.value.blur, + }); + }); }); diff --git a/src/js/themes/darkTheme/darkTheme.ts b/src/js/themes/darkTheme/darkTheme.ts index cd2f29a1..5084cbab 100644 --- a/src/js/themes/darkTheme/darkTheme.ts +++ b/src/js/themes/darkTheme/darkTheme.ts @@ -1,8 +1,10 @@ import { Theme } from '../types'; import { typography } from '../../typography'; import { colors } from './colors'; +import { shadows } from './shadows'; export const darkTheme: Theme = { colors, typography, + shadows, }; diff --git a/src/js/themes/darkTheme/shadows.test.ts b/src/js/themes/darkTheme/shadows.test.ts new file mode 100644 index 00000000..ec0cfda8 --- /dev/null +++ b/src/js/themes/darkTheme/shadows.test.ts @@ -0,0 +1,86 @@ +/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports */ +import { shadows } from './shadows'; + +const designTokens = require('../../../figma/tokens.json'); + +describe('Shadows', () => { + it('js tokens shadows.size.xs matches figma tokens shadows.xs', () => { + expect(shadows.size.xs.shadowColor).toStrictEqual( + designTokens.dark.shadows.xs.value.color, + ); + + expect(shadows.size.xs.shadowOffset.width).toStrictEqual( + designTokens.dark.shadows.xs.value.x, + ); + + expect(shadows.size.xs.shadowOffset.height).toStrictEqual( + designTokens.dark.shadows.xs.value.y, + ); + + expect(shadows.size.xs.shadowOpacity).toStrictEqual(1); + + expect(shadows.size.xs.shadowRadius).toStrictEqual( + designTokens.dark.shadows.xs.value.blur, + ); + }); + + it('js tokens shadows.size.sm matches figma tokens shadows.sm', () => { + expect(shadows.size.sm.shadowColor).toStrictEqual( + designTokens.dark.shadows.sm.value.color, + ); + + expect(shadows.size.sm.shadowOffset.width).toStrictEqual( + designTokens.dark.shadows.sm.value.x, + ); + + expect(shadows.size.sm.shadowOffset.height).toStrictEqual( + designTokens.dark.shadows.sm.value.y, + ); + + expect(shadows.size.sm.shadowOpacity).toStrictEqual(1); + + expect(shadows.size.sm.shadowRadius).toStrictEqual( + designTokens.dark.shadows.sm.value.blur, + ); + }); + + it('js tokens shadows.size.md matches figma tokens shadows.md', () => { + expect(shadows.size.md.shadowColor).toStrictEqual( + designTokens.dark.shadows.md.value.color, + ); + + expect(shadows.size.md.shadowOffset.width).toStrictEqual( + designTokens.dark.shadows.md.value.x, + ); + + expect(shadows.size.md.shadowOffset.height).toStrictEqual( + designTokens.dark.shadows.md.value.y, + ); + + expect(shadows.size.md.shadowOpacity).toStrictEqual(1); + + expect(shadows.size.md.shadowRadius).toStrictEqual( + designTokens.dark.shadows.md.value.blur, + ); + }); + + it('js tokens shadows.size.lg matches figma tokens shadows.lg', () => { + expect(shadows.size.lg.shadowColor).toStrictEqual( + designTokens.dark.shadows.lg.value.color, + ); + + expect(shadows.size.lg.shadowOffset.width).toStrictEqual( + designTokens.dark.shadows.lg.value.x, + ); + + expect(shadows.size.lg.shadowOffset.height).toStrictEqual( + designTokens.dark.shadows.lg.value.y, + ); + + expect(shadows.size.lg.shadowOpacity).toStrictEqual(1); + + expect(shadows.size.lg.shadowRadius).toStrictEqual( + designTokens.dark.shadows.lg.value.blur, + ); + }); +}); diff --git a/src/js/themes/darkTheme/shadows.ts b/src/js/themes/darkTheme/shadows.ts new file mode 100644 index 00000000..341b36b8 --- /dev/null +++ b/src/js/themes/darkTheme/shadows.ts @@ -0,0 +1,43 @@ +import { ThemeShadows } from '../types'; +import { colors } from './colors'; + +export const shadows: ThemeShadows = { + size: { + xs: { + shadowColor: colors.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 4, + }, + sm: { + shadowColor: colors.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 8, + }, + md: { + shadowColor: colors.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 16, + }, + lg: { + shadowColor: colors.shadow.default, + shadowOffset: { + width: 0, + height: 8, + }, + shadowOpacity: 1, + shadowRadius: 40, + }, + }, +}; diff --git a/src/js/themes/lightTheme/colors.ts b/src/js/themes/lightTheme/colors.ts index f67427c9..b66b7496 100644 --- a/src/js/themes/lightTheme/colors.ts +++ b/src/js/themes/lightTheme/colors.ts @@ -28,12 +28,16 @@ export const colors: ThemeColors = { inverse: '#FCFCFC', alternative: '#000000CC', }, + shadow: { + default: '#0000001a', + }, primary: { default: '#037DD6', alternative: '#0260A4', muted: '#037DD619', inverse: '#FCFCFC', disabled: '#037DD680', + shadow: '#037dd633', }, secondary: { default: '#F66A0A', @@ -48,6 +52,7 @@ export const colors: ThemeColors = { muted: '#D73A4919', inverse: '#FCFCFC', disabled: '#D73A4980', + shadow: '#d73a4966', }, warning: { default: '#F66A0A', diff --git a/src/js/themes/lightTheme/lightTheme.test.ts b/src/js/themes/lightTheme/lightTheme.test.ts index 3bb72b98..207ecd5e 100644 --- a/src/js/themes/lightTheme/lightTheme.test.ts +++ b/src/js/themes/lightTheme/lightTheme.test.ts @@ -15,4 +15,16 @@ describe('Light Theme', () => { designTokens.global.fontFamilies['euclid-circular-b'].value, ); }); + + it('shadow tokens are exported from lightTheme by checking first shadow size object', () => { + expect(lightTheme.shadows.size.xs).toStrictEqual({ + shadowColor: designTokens.light.shadows.xs.value.color, + shadowOffset: { + width: designTokens.light.shadows.xs.value.x, + height: designTokens.light.shadows.xs.value.y, + }, + shadowOpacity: 1, + shadowRadius: designTokens.light.shadows.xs.value.blur, + }); + }); }); diff --git a/src/js/themes/lightTheme/lightTheme.ts b/src/js/themes/lightTheme/lightTheme.ts index b5fc19c1..f1ec1231 100644 --- a/src/js/themes/lightTheme/lightTheme.ts +++ b/src/js/themes/lightTheme/lightTheme.ts @@ -1,8 +1,10 @@ import { Theme } from '../types'; import { typography } from '../../typography'; +import { shadows } from './shadows'; import { colors } from './colors'; export const lightTheme: Theme = { colors, typography, + shadows, }; diff --git a/src/js/themes/lightTheme/shadows.test.ts b/src/js/themes/lightTheme/shadows.test.ts new file mode 100644 index 00000000..e2f65cd0 --- /dev/null +++ b/src/js/themes/lightTheme/shadows.test.ts @@ -0,0 +1,86 @@ +/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports */ +import { shadows } from './shadows'; + +const designTokens = require('../../../figma/tokens.json'); + +describe('Shadows', () => { + it('js tokens shadows.size.xs matches figma tokens shadows.xs', () => { + expect(shadows.size.xs.shadowColor).toStrictEqual( + designTokens.light.shadows.xs.value.color, + ); + + expect(shadows.size.xs.shadowOffset.width).toStrictEqual( + designTokens.light.shadows.xs.value.x, + ); + + expect(shadows.size.xs.shadowOffset.height).toStrictEqual( + designTokens.light.shadows.xs.value.y, + ); + + expect(shadows.size.xs.shadowOpacity).toStrictEqual(1); + + expect(shadows.size.xs.shadowRadius).toStrictEqual( + designTokens.light.shadows.xs.value.blur, + ); + }); + + it('js tokens shadows.size.sm matches figma tokens shadows.sm', () => { + expect(shadows.size.sm.shadowColor).toStrictEqual( + designTokens.light.shadows.sm.value.color, + ); + + expect(shadows.size.sm.shadowOffset.width).toStrictEqual( + designTokens.light.shadows.sm.value.x, + ); + + expect(shadows.size.sm.shadowOffset.height).toStrictEqual( + designTokens.light.shadows.sm.value.y, + ); + + expect(shadows.size.sm.shadowOpacity).toStrictEqual(1); + + expect(shadows.size.sm.shadowRadius).toStrictEqual( + designTokens.light.shadows.sm.value.blur, + ); + }); + + it('js tokens shadows.size.md matches figma tokens shadows.md', () => { + expect(shadows.size.md.shadowColor).toStrictEqual( + designTokens.light.shadows.md.value.color, + ); + + expect(shadows.size.md.shadowOffset.width).toStrictEqual( + designTokens.light.shadows.md.value.x, + ); + + expect(shadows.size.md.shadowOffset.height).toStrictEqual( + designTokens.light.shadows.md.value.y, + ); + + expect(shadows.size.md.shadowOpacity).toStrictEqual(1); + + expect(shadows.size.md.shadowRadius).toStrictEqual( + designTokens.light.shadows.md.value.blur, + ); + }); + + it('js tokens shadows.size.lg matches figma tokens shadows.lg', () => { + expect(shadows.size.lg.shadowColor).toStrictEqual( + designTokens.light.shadows.lg.value.color, + ); + + expect(shadows.size.lg.shadowOffset.width).toStrictEqual( + designTokens.light.shadows.lg.value.x, + ); + + expect(shadows.size.lg.shadowOffset.height).toStrictEqual( + designTokens.light.shadows.lg.value.y, + ); + + expect(shadows.size.lg.shadowOpacity).toStrictEqual(1); + + expect(shadows.size.lg.shadowRadius).toStrictEqual( + designTokens.light.shadows.lg.value.blur, + ); + }); +}); diff --git a/src/js/themes/lightTheme/shadows.ts b/src/js/themes/lightTheme/shadows.ts new file mode 100644 index 00000000..341b36b8 --- /dev/null +++ b/src/js/themes/lightTheme/shadows.ts @@ -0,0 +1,43 @@ +import { ThemeShadows } from '../types'; +import { colors } from './colors'; + +export const shadows: ThemeShadows = { + size: { + xs: { + shadowColor: colors.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 4, + }, + sm: { + shadowColor: colors.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 8, + }, + md: { + shadowColor: colors.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 16, + }, + lg: { + shadowColor: colors.shadow.default, + shadowOffset: { + width: 0, + height: 8, + }, + shadowOpacity: 1, + shadowRadius: 40, + }, + }, +}; diff --git a/src/js/themes/types.ts b/src/js/themes/types.ts index d4f12902..1edad9b0 100644 --- a/src/js/themes/types.ts +++ b/src/js/themes/types.ts @@ -1,5 +1,26 @@ import { ThemeTypography } from '../typography'; +interface ShadowShape { + shadowColor: string; + shadowOffset: { + width: number; + height: number; + }; + shadowOpacity: number; + shadowRadius: number; +} + +interface ShadowSizes { + xs: ShadowShape; + sm: ShadowShape; + md: ShadowShape; + lg: ShadowShape; +} + +export interface ThemeShadows { + size: ShadowSizes; +} + export interface ThemeColors { background: { /** @@ -63,6 +84,12 @@ export interface ThemeColors { */ alternative: string; }; + shadow: { + /** + * {string} shadow.default - For neutral shadows + */ + default: string; + }; primary: { /** * {string} primary.default - For primary user action related elements @@ -84,6 +111,10 @@ export interface ThemeColors { * {string} primary.disabled - [Deprecated] Should be used for */ disabled: string; + /** + * {string} primary.shadow - For primary button hover + */ + shadow: string; }; secondary: { /** @@ -128,6 +159,10 @@ export interface ThemeColors { * {string} error.disabled - [Deprecated] Should be used for disabled state */ disabled: string; + /** + * {string} error.shadow - For error danger/critical button hover + */ + shadow: string; }; warning: { /** @@ -200,4 +235,5 @@ export interface ThemeColors { export interface Theme { colors: ThemeColors; typography: ThemeTypography; + shadows: ThemeShadows; }