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..47f56b4f --- /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 page + +## Size + +There are 4 different sizes of shadow in MetaMask. + + + + + +| Size | JS | CSS | +| ------ | ---------------- | ----------------------- | +| **XS** | `shadow.size.xs` | `var(--shadow-size-xs)` | +| **SM** | `shadow.size.sm` | `var(--shadow-size-sm)` | +| **MD** | `shadow.size.md` | `var(--shadow-size-md)` | +| **LG** | `shadow.size.lg` | `var(--shadow-size-lg)` | + +## Color + +As well as the neutral colors for shadow 2 custom 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, colors.shadow.default }` | `box-shadow: var(--shadow-size-xs) var(--color-shadow-default);` | +| **Dropdown** | `dropdownShadow: { ...shadows.size.sm, colors.shadow.default}` | `box-shadow: var(--shadow-size-sm) var(--color-shadow-default);` | +| **Toast** | `toastShadow: { ...shadows.size.md, colors.shadow.default}` | `box-shadow: var(--shadow-size-md) var(--color-shadow-default);` | +| **Modal** | `modalShadow: { ...shadows.size.lg, colors.shadow.default}` | `box-shadow: var(--shadow-size-lg) var(--color-shadow-default);` | +| **Button Primary Hover** | `buttonPrimaryHover: { ...shadows.size.sm, colors.primary.shadow}` | `box-shadow: var(--shadow-size-sm) var(--color-primary-shadow);` | +| **Button Danger Hover** | `buttonDangerHover: { ...shadows.size.sm, colors.primary.shadow}` | `box-shadow: var(--shadow-size-sm) var(--color-error-shadow);` | + +The CSS-in-JS `shadows.size` objects for React Native do contain a default color but colors should be overridden so the correct shadow colors are displayed in both light and dark themes. + +The `xs` shadow object from `shadows` + +``` +size: { + xs: { + shadowColor: colors.light.shadow.default, // should not be used + 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..2654eecc --- /dev/null +++ b/docs/Shadows.stories.tsx @@ -0,0 +1,264 @@ +import { ComponentStoryFn, ComponentMeta } from '@storybook/react'; +import tokens from '../src/figma/tokens.json'; +import { Text } from './components'; + +import README from './Shadows.mdx'; + +export default { + title: 'Shadows/Shadows', + parameters: { + docs: { + page: README, + }, + }, +}; +// as ComponentMeta; + +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 6d58e3d2..9f50ccea 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" + } } } } diff --git a/src/js/colors/colors.test.ts b/src/js/colors/colors.test.ts index c345585a..a300c6be 100644 --- a/src/js/colors/colors.test.ts +++ b/src/js/colors/colors.test.ts @@ -78,6 +78,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, @@ -108,6 +126,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, @@ -156,6 +180,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, @@ -308,6 +338,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, @@ -332,6 +380,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, @@ -380,6 +434,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/colors/colors.ts b/src/js/colors/colors.ts index 977ebb69..9e68a598 100644 --- a/src/js/colors/colors.ts +++ b/src/js/colors/colors.ts @@ -24,11 +24,14 @@ import { Colors } from './types'; * @property {string} overlay.alternative - For a stronger shading layer option behind modality screens * @property {string} overlay.inverse - [Deprecated] Should be used for elements over an overlay * + * @property {string} shadow.default - For neutral shadows + * * @property {string} primary.default - For primary user action related elements * @property {string} primary.alternative - For the \"pressed\" state of interactive primary elements * @property {string} primary.muted - For lowest contrast background used in primary elements * @property {string} primary.disabled - [Deprecated] Should be used for disabled state * @property {string} primary.inverse - For elements used on top of primary/default. (Example: label of primary button, check in a checkbox) + * @property {string} primary.shadow - For primary button hover * * @property {string} secondary.default - [Deprecated] Should be used for any secondary actions. It should not be used for any negative connotations such as warnings or errors as it is quite closely tied to the MetaMask Fox * @property {string} secondary.alternative - [Deprecated] Should be used as an alternative to secondary.default for things such as hover states @@ -41,6 +44,7 @@ import { Colors } from './types'; * @property {string} error.muted - For lowest contrast background used in high-level alert danger/critical elements. (Example: notification background) * @property {string} error.disabled - [Deprecated] Should be used for disabled state * @property {string} error.inverse - For elements used on top of error/default (Example: label of danger/critical button) + * @property {string} error.shadow - For error danger/critical button hover * * @property {string} warning.default - For low-mid level alert elements. Used for text, background, icon or border * @property {string} warning.alternative - [Deprecated] Should be used as an alternative to warning/default for things like hover or pressed states @@ -86,12 +90,16 @@ export const colors: Colors = { inverse: '#FCFCFC', alternative: '#000000CC', }, + shadow: { + default: '#0000001a', + }, primary: { default: '#037DD6', alternative: '#0260A4', muted: '#037DD619', inverse: '#FCFCFC', disabled: '#037DD680', + shadow: '#037dd633', }, secondary: { default: '#F66A0A', @@ -106,6 +114,7 @@ export const colors: Colors = { muted: '#D73A4919', inverse: '#FCFCFC', disabled: '#D73A4980', + shadow: '#d73a4966', }, warning: { default: '#F66A0A', @@ -153,12 +162,16 @@ export const colors: Colors = { inverse: '#FCFCFC', alternative: '#000000CC', }, + shadow: { + default: '#00000066', + }, primary: { default: '#1098FC', alternative: '#43AEFC', muted: '#1098FC26', inverse: '#FCFCFC', disabled: '#1098FC80', + shadow: '#1098FC66', }, secondary: { default: '#F8883B', @@ -173,6 +186,7 @@ export const colors: Colors = { muted: '#D73A4926', inverse: '#FCFCFC', disabled: '#D73A4980', + shadow: '#D73A4966', }, warning: { default: '#FFD33D', diff --git a/src/js/colors/types.ts b/src/js/colors/types.ts index 9937662b..3eab1959 100644 --- a/src/js/colors/types.ts +++ b/src/js/colors/types.ts @@ -22,12 +22,16 @@ interface ThemeColors { inverse: string; alternative: string; }; + shadow: { + default: string; + }; primary: { default: string; alternative: string; muted: string; inverse: string; disabled: string; + shadow: string; }; secondary: { default: string; @@ -42,6 +46,7 @@ interface ThemeColors { muted: string; inverse: string; disabled: string; + shadow: string; }; warning: { default: string; diff --git a/src/js/index.ts b/src/js/index.ts index 3c9e4f2f..392c1535 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -1,2 +1,3 @@ export { colors } from './colors'; export { typography } from './typography'; +export { shadows } from './shadows'; diff --git a/src/js/shadows/index.ts b/src/js/shadows/index.ts new file mode 100644 index 00000000..0598ea06 --- /dev/null +++ b/src/js/shadows/index.ts @@ -0,0 +1 @@ +export { shadows } from './shadows'; diff --git a/src/js/shadows/shadows.test.ts b/src/js/shadows/shadows.test.ts new file mode 100644 index 00000000..436cf941 --- /dev/null +++ b/src/js/shadows/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/shadows/shadows.ts b/src/js/shadows/shadows.ts new file mode 100644 index 00000000..770d5b21 --- /dev/null +++ b/src/js/shadows/shadows.ts @@ -0,0 +1,43 @@ +import { colors } from '../colors'; +import { Shadow } from './types'; + +export const shadows: Shadow = { + size: { + xs: { + shadowColor: colors.light.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 4, + }, + sm: { + shadowColor: colors.light.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 8, + }, + md: { + shadowColor: colors.light.shadow.default, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 1, + shadowRadius: 16, + }, + lg: { + shadowColor: colors.light.shadow.default, + shadowOffset: { + width: 0, + height: 8, + }, + shadowOpacity: 1, + shadowRadius: 40, + }, + }, +}; diff --git a/src/js/shadows/types.ts b/src/js/shadows/types.ts new file mode 100644 index 00000000..b18ff178 --- /dev/null +++ b/src/js/shadows/types.ts @@ -0,0 +1,20 @@ +interface ShadowShape { + shadowColor: string; + shadowOffset: { + width: number; + height: number; + }; + shadowOpacity: number; + shadowRadius: number; +} + +interface ThemeShadow { + xs: ShadowShape; + sm: ShadowShape; + md: ShadowShape; + lg: ShadowShape; +} + +export interface Shadow { + size: ThemeShadow; +}