From 07dae4b338d5b0966fc186fa27a944005a2e3fda Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Tue, 18 Jun 2024 18:10:34 -0400 Subject: [PATCH] feat(components): add helix product typography - Adds the new sizes for type in the helix product system - Adds a dummy component under "Design Tokens" in storybook so you can view all the text Closes EXEC-566 Make the storybook display all the stories --- .../Typography/Typography.stories.tsx | 230 ++++++++++++++++++ components/src/helix-design-system/index.ts | 1 + .../src/helix-design-system/product/index.ts | 1 + .../helix-design-system/product/typography.ts | 141 +++++++++++ 4 files changed, 373 insertions(+) create mode 100644 app/src/DesignTokens/Typography/Typography.stories.tsx create mode 100644 components/src/helix-design-system/product/index.ts create mode 100644 components/src/helix-design-system/product/typography.ts diff --git a/app/src/DesignTokens/Typography/Typography.stories.tsx b/app/src/DesignTokens/Typography/Typography.stories.tsx new file mode 100644 index 000000000000..7558d7562352 --- /dev/null +++ b/app/src/DesignTokens/Typography/Typography.stories.tsx @@ -0,0 +1,230 @@ +import * as React from 'react' +import { css } from 'styled-components' +import type { FlattenSimpleInterpolation } from 'styled-components' +import { + ALIGN_CENTER, + Box, + DIRECTION_COLUMN, + Flex, + SPACING, + Text, + PRODUCT, + TYPOGRAPHY, +} from '@opentrons/components' + +import type { Story, Meta } from '@storybook/react' + +const fontStyles = { + 'Helix Product (Desktop)': [ + ['Display', 'Bold'], + ['HeadingLarge', 'Regular'], + ['HeadingLarge', 'Bold'], + ['HeadingMedium', 'Medium'], + ['HeadingSmall', 'Regular'], + ['HeadingSmall', 'Bold'], + ['BodyLarge', 'Medium'], + ['BodyLarge', 'Regular'], + ['BodyDefault', 'Medium'], + ['BodyDefault', 'Regular'], + ['Caption', 'Medium'], + ['Caption', 'Regular'], + ['Code', 'Regular'], + ], + ODD: [ + ['level1Header', ''], + ['level2Header', 'Bold'], + ['level2Header', 'SemiBold'], + ['level2Header', 'Regular'], + ['level3Header', 'Bold'], + ['level3Header', 'SemiBold'], + ['level3Header', 'Regular'], + ['level4Header', 'Bold'], + ['level4Header', 'SemiBold'], + ['level4Header', 'Regular'], + ['bodyText', 'Bold'], + ['bodyText', 'SemiBold'], + ['bodyText', 'Regular'], + ['smallBodyText', 'Bold'], + ['smallBodyText', 'SemiBold'], + ['smallBodyText', 'Regular'], + ], + 'Legacy Desktop': [ + ['h1', 'Default'], + ['h2', 'Regular'], + ['h2', 'SemiBold'], + ['h3', 'Regular'], + ['h3', 'SemiBold'], + ['h6', 'Default'], + ['h6', 'SemiBold'], + ['p', 'Regular'], + ['p', 'SemiBold'], + ['label', 'Regular'], + ['label', 'SemiBold'], + ['linkP', 'SemiBold'], + ], +} + +type TypographyStandard = keyof typeof fontStyles + +export default { + title: 'Design Tokens/Typography', + argTypes: { + text: { + type: 'text', + }, + styles: { + control: { + type: 'select', + }, + options: Object.keys(fontStyles), + }, + }, +} as Meta + +interface TypographyStorybookProps { + text: string + styles: TypographyStandard +} + +const convertToPx = (remFormat: string): string => { + const pxVal = Number(remFormat.replace('rem', '')) * 16 + return `${pxVal}px` +} +const styleForPairForHelix = (style: string, weight: string): string => { + const fontPayload = PRODUCT.TYPOGRAPHY[`fontStyle${style}${weight}`] + return css` + font: ${fontPayload}; + ` +} +const fontSizeForPairForHelix = (style: string, weight: string): string => { + const fontSize = PRODUCT.TYPOGRAPHY[`fontSize${style}${weight}`] + const fontSizeInPx = convertToPx(fontSize) + return `font-size: ${fontSize}/${fontSizeInPx}` +} +const lineHeightForPairForHelix = (style: string, weight: string): string => { + const lineHeight = PRODUCT.TYPOGRAPHY[`lineHeight${style}${weight}`] + const lineHeightInPx = convertToPx(lineHeight) + return `line-height: ${lineHeight}/${lineHeightInPx}` +} +const fontWeightForPairForHelix = (style: string, weight: string): string => { + const fontWeight = PRODUCT.TYPOGRAPHY[`fontWeight${style}${weight}`] + return `font-weight: ${fontWeight}` +} + +const styleForPairForLegacy = (style: string, weight: string): string => { + return TYPOGRAPHY[`${style}${weight}`] +} + +const fontSizeForPairForLegacy = (style: string, weight: string): string => { + const stylePayload = styleForPairForLegacy(style, weight) + const sizeStr = valueFromFlattenedInterp(stylePayload, 'font-size:') + const sizeInPx = convertToPx(sizeStr) + + return `font-size: ${sizeStr}/${sizeInPx}` +} + +const lineHeightForPairForLegacy = (style: string, weight: string): string => { + const stylePayload = styleForPairForLegacy(style, weight) + const sizeStr = valueFromFlattenedInterp(stylePayload, 'line-height:') + const sizeInPx = convertToPx(sizeStr) + return `line-height: ${sizeStr}/${sizeInPx}` +} + +const fontWeightForPairForLegacy = (style: string, weight: string): string => { + const stylePayload = styleForPairForLegacy(style, weight) + const fontWeight = valueFromFlattenedInterp(stylePayload, 'font-weight:') + return `font-weight: ${fontWeight}` +} + +const valueFromFlattenedInterp = ( + style: FlattenSimpleInterpolation, + valueName: str +): string => { + return style.reduce( + ([sawKey, value]: [boolean, null | string], el) => { + const thisEl = el.trim() + if (sawKey && value == null) { + return [sawKey, el] + } + if (sawKey && value != null) { + return [sawKey, value] + } + if (thisEl.includes(valueName)) { + return [true, null] + } + return [false, null] + }, + [false, null] + )[1] +} + +const styleForPair = ( + style: string, + weight: string, + which: TypographyStandard +): string => + which === 'Helix Product (Desktop)' + ? styleForPairForHelix(style, weight) + : styleForPairForLegacy(style, weight) + +const fontSizeForPair = ( + style: string, + weight: string, + which: TypographyStandard +): string => + which === 'Helix Product (Desktop)' + ? fontSizeForPairForHelix(style, weight) + : fontSizeForPairForLegacy(style, weight) + +const lineHeightForPair = ( + style: string, + weight: string, + which: TypographyStandard +): string => + which === 'Helix Product (Desktop)' + ? lineHeightForPairForHelix(style, weight) + : lineHeightForPairForLegacy(style, weight) + +const fontWeightForPair = ( + style: string, + weight: string, + which: TypographyStandard +): string => + which === 'Helix Product (Desktop)' + ? fontWeightForPairForHelix(style, weight) + : fontWeightForPairForLegacy(style, weight) + +const Template: Story = args => { + const fonts = fontStyles[args.styles] + return ( + + {fonts.map(([style, weight]) => ( + + + {`${style} ${weight} (${fontWeightForPair( + style, + weight, + args.styles + )}, ${fontSizeForPair( + style, + weight, + args.styles + )}, ${lineHeightForPair(style, weight, args.styles)}): ${ + args.text + }`} + + + ))} + + ) +} + +export const AllTypographyStyles = Template.bind({}) +AllTypographyStyles.args = { + text: 'The quick brown fox jumped over the lazy dog.', + styles: 'Helix Product (Desktop)', +} diff --git a/components/src/helix-design-system/index.ts b/components/src/helix-design-system/index.ts index 60e3c024003d..566734b06330 100644 --- a/components/src/helix-design-system/index.ts +++ b/components/src/helix-design-system/index.ts @@ -1,2 +1,3 @@ export * as COLORS from './colors' export * as BORDERS from './borders' +export * as PRODUCT from './product' diff --git a/components/src/helix-design-system/product/index.ts b/components/src/helix-design-system/product/index.ts new file mode 100644 index 000000000000..b37d4048d4c4 --- /dev/null +++ b/components/src/helix-design-system/product/index.ts @@ -0,0 +1 @@ +export * as TYPOGRAPHY from './typography' diff --git a/components/src/helix-design-system/product/typography.ts b/components/src/helix-design-system/product/typography.ts new file mode 100644 index 000000000000..8350edfc0242 --- /dev/null +++ b/components/src/helix-design-system/product/typography.ts @@ -0,0 +1,141 @@ +/** + * Note: these values are only currently correct for desktop and should not be used on ODD. + */ + +// Valid for most typography styles +export const fontFamily = 'Public Sans' + +export const fontWeightRegular = '400' +export const fontWeightSemiBold = '600' +export const fontWeightBold = '700' + +// Display styles +const fontFamilyDisplay = fontFamily +const fontSizeDisplay = '2.4375rem' // 39px +const lineHeightDisplay = '3rem' // 48px + +// Display-Bold +export const fontSizeDisplayBold = fontSizeDisplay +export const lineHeightDisplayBold = lineHeightDisplay +export const fontFamilyDisplayBold = fontFamilyDisplay +export const fontWeightDisplayBold = fontWeightBold +export const fontStyleDisplayBold = `${fontWeightDisplayBold} ${fontSizeDisplayBold}/${lineHeightDisplayBold} ${fontFamilyDisplayBold}` + +// Heading-Large +const fontSizeHeadingLarge = '1.625rem' // 26px +const lineHeightHeadingLarge = '2rem' // 32px +const fontFamilyHeadingLarge = fontFamily + +// Heading-Large-Regular +export const fontFamilyHeadingLargeRegular = fontFamilyHeadingLarge +export const fontSizeHeadingLargeRegular = fontSizeHeadingLarge +export const lineHeightHeadingLargeRegular = lineHeightHeadingLarge +export const fontWeightHeadingLargeRegular = fontWeightRegular +export const fontStyleHeadingLargeRegular = `${fontWeightHeadingLargeRegular} ${fontSizeHeadingLargeRegular}/${lineHeightHeadingLargeRegular} ${fontFamilyHeadingLargeRegular}` + +// Heading-Large-Bold +export const fontFamilyHeadingLargeBold = fontFamilyHeadingLarge +export const fontSizeHeadingLargeBold = fontSizeHeadingLarge +export const lineHeightHeadingLargeBold = lineHeightHeadingLarge +export const fontWeightHeadingLargeBold = fontWeightBold +export const fontStyleHeadingLargeBold = `${fontWeightHeadingLargeBold} ${fontSizeHeadingLargeBold}/${lineHeightHeadingLargeBold} ${fontFamilyHeadingLargeBold}` + +// Heading-Medium +const fontSizeHeadingMedium = '1.4375rem' // 23px +const lineHeightHeadingMedium = '1.75rem' // 28px +const fontFamilyHeadingMedium = fontFamily + +// Heading-Medium-Regular +export const fontSizeHeadingMediumMedium = fontSizeHeadingMedium +export const lineHeightHeadingMediumMedium = lineHeightHeadingMedium +export const fontFamilyHeadingMediumMedium = fontFamilyHeadingMedium +export const fontWeightHeadingMediumMedium = fontWeightSemiBold +export const fontStyleHeadingMediumMedium = `${fontWeightHeadingMediumMedium} ${fontSizeHeadingMediumMedium}/${lineHeightHeadingMediumMedium} ${fontFamilyHeadingMediumMedium}` + +// Heading-Small +const fontSizeHeadingSmall = '1.125rem' // 18px +const lineHeightHeadingSmall = '1.5rem' // 24px +const fontFamilyHeadingSmall = fontFamily + +// Heading-Small-Regular +export const fontSizeHeadingSmallRegular = fontSizeHeadingSmall +export const lineHeightHeadingSmallRegular = lineHeightHeadingSmall +export const fontFamilyHeadingSmallRegular = fontFamilyHeadingSmall +export const fontWeightHeadingSmallRegular = '600' +export const fontStyleHeadingSmallRegular = `${fontWeightHeadingSmallRegular} ${fontSizeHeadingSmallRegular}/${lineHeightHeadingSmallRegular} ${fontFamilyHeadingSmallRegular}` + +// Heading-Small-Bold +export const fontSizeHeadingSmallBold = fontSizeHeadingSmall +export const lineHeightHeadingSmallBold = lineHeightHeadingSmall +export const fontFamilyHeadingSmallBold = fontFamilyHeadingSmall +export const fontWeightHeadingSmallBold = fontWeightBold +export const fontStyleHeadingSmallBold = `${fontWeightHeadingSmallBold} ${fontSizeHeadingSmallBold}/${lineHeightHeadingSmallBold} ${fontFamilyHeadingSmallBold}` + +// Body-Large +const fontSizeBodyLarge = '1rem' // 16px +const lineHeightBodyLarge = '1.5rem' // 24px +const fontFamilyBodyLarge = fontFamily + +// Body-Large-Medium +export const fontSizeBodyLargeMedium = fontSizeBodyLarge +export const lineHeightBodyLargeMedium = lineHeightBodyLarge +export const fontFamilyBodyLargeMedium = fontFamilyBodyLarge +export const fontWeightBodyLargeMedium = fontWeightSemiBold +export const fontStyleBodyLargeMedium = `${fontWeightBodyLargeMedium} ${fontSizeBodyLargeMedium}/${lineHeightBodyLargeMedium} ${fontFamilyBodyLargeMedium}` + +// Body-Large-Regular +export const fontSizeBodyLargeRegular = fontSizeBodyLarge +export const lineHeightBodyLargeRegular = lineHeightBodyLarge +export const fontFamilyBodyLargeRegular = fontFamilyBodyLarge +export const fontWeightBodyLargeRegular = fontWeightRegular +export const fontStyleBodyLargeRegular = `${fontWeightBodyLargeRegular} ${fontSizeBodyLargeRegular}/${lineHeightBodyLargeRegular} ${fontFamilyBodyLargeRegular}` + +// Body-Default +const fontSizeBodyDefault = '0.875rem' // 14px +const lineHeightBodyDefault = '1.25rem' // 20px +const fontFamilyBodyDefault = fontFamily + +// Body-Default-Medium +export const fontSizeBodyDefaultMedium = fontSizeBodyDefault +export const lineHeightBodyDefaultMedium = lineHeightBodyDefault +export const fontFamilyBodyDefaultMedium = fontFamilyBodyDefault +export const fontWeightBodyDefaultMedium = fontWeightSemiBold +export const fontStyleBodyDefaultMedium = `${fontWeightBodyDefaultMedium} ${fontSizeBodyDefaultMedium}/${lineHeightBodyDefaultMedium} ${fontFamilyBodyDefaultMedium}` + +// Body-Default-Regular +export const fontSizeBodyDefaultRegular = fontSizeBodyDefault +export const lineHeightBodyDefaultRegular = lineHeightBodyDefault +export const fontFamilyBodyDefaultRegular = fontFamilyBodyDefault +export const fontWeightBodyDefaultRegular = fontWeightRegular +export const fontStyleBodyDefaultRegular = `${fontWeightBodyDefaultRegular} ${fontSizeBodyDefaultRegular}/${lineHeightBodyDefaultRegular} ${fontFamilyBodyDefaultRegular}` + +// Caption +const fontSizeCaption = '0.8125rem' // 13px +const lineHeightCaption = '1rem' // 16px +const fontFamilyCaption = 'Public Sans' + +// Caption-Medium +export const fontSizeCaptionMedium = fontSizeCaption +export const lineHeightCaptionMedium = lineHeightCaption +export const fontFamilyCaptionMedium = fontFamilyCaption +export const fontWeightCaptionMedium = '500' +export const fontStyleCaptionMedium = `${fontWeightCaptionMedium} ${fontSizeCaptionMedium}/${lineHeightCaptionMedium} ${fontFamilyCaptionMedium}` + +// Caption-Regular +export const fontSizeCaptionRegular = fontSizeCaption +export const lineHeightCaptionRegular = lineHeightCaption +export const fontFamilyCaptionRegular = fontFamilyCaption +export const fontWeightCaptionRegular = '400' +export const fontStyleCaptionRegular = `${fontWeightCaptionRegular} ${fontSizeCaptionRegular}/${lineHeightCaptionRegular} ${fontFamilyCaptionRegular}` + +// Code +const fontSizeCode = '0.75rem' // 12px +const lineHeightCode = '1.25rem' // 20px +const fontFamilyCode = 'Reddit Mono' + +// Code-Regular +export const fontSizeCodeRegular = fontSizeCode +export const lineHeightCodeRegular = lineHeightCode +export const fontFamilyCodeRegular = fontFamilyCode +export const fontWeightCodeRegular = fontWeightRegular +export const fontStyleCodeRegular = `${fontWeightCodeRegular} ${fontSizeCodeRegular}/${lineHeightCodeRegular} ${fontFamilyCodeRegular}`