feat: update design tokens

This commit is contained in:
Hossein Mehrabi 2023-02-13 13:13:50 +03:30
parent 45b83041c8
commit a6b437f26f
11 changed files with 317 additions and 329 deletions

View File

@ -14,7 +14,7 @@ export const parameters: Parameters = {
default: 'light',
values: Object.entries(defaultThemes).map(([name, theme]) => ({
name,
value: theme.palette.background.primary,
value: `rgb(${theme.palette.secondary})`,
})),
},
viewport: {

View File

@ -1,23 +1,28 @@
import { DecoratorFunction, useGlobals } from '@storybook/addons'
import React, { useEffect } from 'react'
import { defaultThemes, ThemeProvider } from '../src'
import { defaultThemes, Theme, ThemeProvider } from '../src'
export const withTheme: DecoratorFunction = (Story, context) => {
const StoryComponent = Story as any as React.ComponentType
const themeName = context.globals?.theme ?? 'light'
const theme = defaultThemes[themeName]
const theme = defaultThemes[themeName] as Theme
const [globals, setGlobals] = useGlobals()
useEffect(() => {
setGlobals({
...globals,
backgrounds: {
...(globals.background ?? {}),
value: theme.palette.background.primary,
},
})
const background = (context.parameters.backgrounds?.values ?? []).find(
(value) => value.name === themeName,
)?.value
globals.backgrounds?.value !== background &&
setGlobals({
...globals,
backgrounds: {
...(globals.background ?? {}),
value: background,
},
})
}, [theme])
return (

View File

@ -1,47 +1,38 @@
import { css } from '@emotion/react'
import { withTheme } from '../Theme/withTheme'
import { buttonClasses } from './Button.classes'
export const ButtonStyles = withTheme(
(theme) => css`
.${buttonClasses.root} {
width: auto;
color: var(--lsd-text-primary);
background: none;
border: 1px solid var(--lsd-surface-primary);
export const ButtonStyles = css`
.${buttonClasses.root} {
width: auto;
color: rgb(var(--lsd-text-primary));
background: none;
border: 1px solid rgb(var(--lsd-border-primary));
cursor: pointer;
padding: 6px 24px;
cursor: pointer;
padding: 6px 24px;
}
@media (max-width: ${theme.breakpoints.lg.width}px) {
color: red;
border-color: red;
.${buttonClasses.disabled} {
cursor: default;
opacity: 0.34;
}
.${buttonClasses.large} {
padding: 10px 40px;
}
.${buttonClasses.medium} {
}
.${buttonClasses.small} {
padding: 6px 12px;
}
.${buttonClasses.root}:hover {
&:not(.${buttonClasses.disabled}) {
.${buttonClasses.text} {
text-decoration: underline;
}
}
.${buttonClasses.disabled} {
cursor: default;
color: var(--lsd-surface-disabled);
border-color: var(--lsd-surface-disabled);
}
.${buttonClasses.large} {
padding: 10px 40px;
}
.${buttonClasses.medium} {
}
.${buttonClasses.small} {
padding: 6px 12px;
}
.${buttonClasses.root}:hover {
&:not(.${buttonClasses.disabled}) {
.${buttonClasses.text} {
text-decoration: underline;
}
}
}
`,
)
}
`

View File

@ -1,14 +1,17 @@
import { Global } from '@emotion/react'
import { Global, SerializedStyles } from '@emotion/react'
import React, { useMemo } from 'react'
import { ButtonStyles } from '../Button/Button.styles'
import { defaultThemes, Theme } from '../Theme'
import { defaultThemes, Theme, withTheme } from '../Theme'
const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
[ButtonStyles]
export const CSSBaseline: React.FC<{ theme?: Theme }> = ({
theme = defaultThemes.light,
}) => {
const styles = useMemo(
() =>
[ButtonStyles]
componentStyles
.map((style) => (typeof style === 'function' ? style(theme) : style))
.map((style) => <Global key={style.name} styles={style} />),
[theme],

View File

@ -7,173 +7,140 @@ export const baseTheme: Theme = {
xs: {
width: 0,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
display1: {},
display2: {},
h1: {},
h2: {},
h3: {},
h4: {},
h5: {},
h6: {},
body1: {},
body2: {},
body3: {},
label1: {},
label2: {},
subtitle1: {},
subtitle2: {},
},
},
sm: {
width: 400,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
display1: {},
display2: {},
h1: {},
h2: {},
h3: {},
h4: {},
h5: {},
h6: {},
body1: {},
body2: {},
body3: {},
label1: {},
label2: {},
subtitle1: {},
subtitle2: {},
},
},
md: {
width: 768,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
display1: {},
display2: {},
h1: {},
h2: {},
h3: {},
h4: {},
h5: {},
h6: {},
body1: {},
body2: {},
body3: {},
label1: {},
label2: {},
subtitle1: {},
subtitle2: {},
},
},
lg: {
width: 1024,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
display1: {},
display2: {},
h1: {},
h2: {},
h3: {},
h4: {},
h5: {},
h6: {},
body1: {},
body2: {},
body3: {},
label1: {},
label2: {},
subtitle1: {},
subtitle2: {},
},
},
xl: {
width: 1200,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
display1: {},
display2: {},
h1: {},
h2: {},
h3: {},
h4: {},
h5: {},
h6: {},
body1: {},
body2: {},
body3: {},
label1: {},
label2: {},
subtitle1: {},
subtitle2: {},
},
},
},
typography: {
headlineLg: {
fontSize: '2.875rem',
lineHeight: '3.25rem',
},
headlineMd: {
fontSize: '1.75rem',
lineHeight: '2.25rem',
},
headlineStd: {
fontSize: '2rem',
lineHeight: '2.5rem',
},
headlineSm: {
fontSize: '1.5rem',
lineHeight: '2rem',
},
titleLg: {
fontSize: '1.375rem',
lineHeight: '1.75rem',
},
titleMd: {
fontSize: '1rem',
lineHeight: '1.5rem',
},
titleSm: {
fontSize: '0.875rem',
lineHeight: '1.25rem',
},
bodyLg: {
fontSize: '1rem',
lineHeight: '1.5rem',
},
bodyMd: {
fontSize: '0.875rem',
lineHeight: '1.25rem',
},
bodySm: {
fontSize: '0.75rem',
lineHeight: '1rem',
},
labelLg: {
fontSize: '0.875rem',
lineHeight: '1.25rem',
},
labelMd: {
fontSize: '0.875rem',
lineHeight: '1.25rem',
},
labelSm: {
fontSize: '0.75rem',
lineHeight: '1rem',
},
display1: { fontSize: '5.625rem', lineHeight: '6.125rem' },
display2: { fontSize: '3.5625rem', lineHeight: '4rem' },
h1: { fontSize: '2.875rem', lineHeight: '3.25rem' },
h2: { fontSize: '2.25rem', lineHeight: '2.75rem' },
h3: { fontSize: '2rem', lineHeight: '2.5rem' },
h4: { fontSize: '1.75rem', lineHeight: '2.25rem' },
h5: { fontSize: '1.5rem', lineHeight: '2rem' },
h6: { fontSize: '1.375rem', lineHeight: '1.75rem' },
subtitle1: { fontSize: '1rem', lineHeight: '1.5rem' },
subtitle2: { fontSize: '0.875rem', lineHeight: '1.25rem' },
body1: { fontSize: '1rem', lineHeight: '1.5rem' },
body2: { fontSize: '0.875rem', lineHeight: '1.25rem' },
body3: { fontSize: '0.75rem', lineHeight: '1rem' },
label1: { fontSize: '0.875rem', lineHeight: '1.25rem' },
label2: { fontSize: '0.75rem', lineHeight: '1rem' },
},
palette: {
background: {
primary: 'rgba(255, 255, 255, 1)',
secondary: 'rgba(0, 0, 0, 1)',
},
border: {
primary: 'rgba(51, 51, 56, 1)',
secondary: 'rgba(255, 255, 255, 1)',
tertiary: 'rgba(223, 223, 226, 1)',
},
primary: '0, 0, 0',
secondary: '255, 255, 255',
surface: {
primary: 'rgba(0, 0, 0, 1)',
secondary: 'rgba(0, 0, 0, 0.34)',
tertiary: 'rgba(0, 0, 0, 0.2)',
disabled: 'rgba(168, 168, 168, 1)',
primary: '255, 255, 255',
secondary: '0, 0, 0',
},
text: {
primary: 'rgba(0, 0, 0, 1)',
secondary: 'rgba(0, 0, 0, 0.34)',
placeholder: 'rgba(0, 0, 0, 0.34)',
disabled: 'rgba(0, 0, 0, 0.34)',
primary: '0, 0, 0',
secondary: '255, 255, 255',
tertiary: '0, 0, 0, 0.34',
},
icons: {
primary: 'rgba(0, 0, 0, 1)',
disabled: 'rgba(0, 0, 0, 0.34)',
border: {
primary: '0, 0, 0',
secondary: '255, 255, 255',
},
icon: {
primary: '0, 0, 0',
secondary: '255, 255, 255',
},
},
globalStyles: css``,

View File

@ -10,21 +10,36 @@ export const LSD_NAMESPACE = 'lsd'
export const THEME_BREAKPOINTS = ['xs', 'sm', 'md', 'lg', 'xl'] as Breakpoints[]
export const THEME_TYPOGRAPHY_VARIANTS = [
'headlineLg',
'headlineStd',
'headlineMd',
'headlineSm',
'titleLg',
'titleMd',
'titleSm',
'bodySm',
'bodyMd',
'bodyLg',
'labelLg',
'labelMd',
'labelSm',
'display1',
'display2',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'subtitle1',
'subtitle2',
'body1',
'body2',
'body3',
'label1',
'label2',
] as TypographyVariants[]
export const THEME_TYPOGRAPHY_ELEMENTS: Partial<
Record<TypographyVariants, string[]>
> = {
h1: ['h1'],
h2: ['h2'],
h3: ['h3'],
h4: ['h4'],
h5: ['h5'],
h6: ['h6'],
body1: ['body'],
label1: ['label'],
}
export const THEME_TYPOGRAPHY_PROPERTIES = [
'fontSize',
'lineHeight',

View File

@ -1,5 +1,4 @@
import { css } from '@emotion/react'
import defaultsDeep from 'lodash/defaultsDeep'
import { pairs } from '../../utils/object.utils'
import { baseTheme } from './baseTheme'
import { THEME_BREAKPOINTS, THEME_TYPOGRAPHY_VARIANTS } from './constants'
@ -9,6 +8,7 @@ import type {
CreateThemeProps,
Theme,
ThemeBreakpoints,
ThemePalette,
TypographyVariants,
VariantThemeProperties,
} from './types'
@ -32,6 +32,9 @@ const createBreakpointStyle = (
...defaultTheme[key][variant],
...theme[key][variant],
...(all?.[index - 1]?.[key]?.[variant] ?? {}),
...(defaultTheme.breakpoints?.[THEME_BREAKPOINTS[index]]?.[key]?.[
variant
] ?? {}),
...(theme.breakpoints?.[THEME_BREAKPOINTS[index]]?.[key]?.[variant] ??
{}),
}))
@ -68,8 +71,34 @@ const createBreakpointStyles = (
).map((styles, index) => [THEME_BREAKPOINTS[index], styles]),
) as ThemeBreakpoints
const createPaletteStyles = (theme: CreateThemeProps, defaultTheme: Theme) =>
defaultsDeep(theme.palette, defaultTheme.palette)
const createPaletteStyles = (theme: CreateThemeProps, defaultTheme: Theme) => {
const primary = theme.palette.primary ?? defaultTheme.palette.primary
const secondary = theme.palette.secondary ?? defaultTheme.palette.secondary
const palette: ThemePalette = {
primary,
secondary,
surface: {
primary: theme.palette.surface?.primary ?? secondary,
secondary: theme.palette.surface?.secondary ?? primary,
},
border: {
primary: theme.palette.border?.primary ?? primary,
secondary: theme.palette.border?.secondary ?? secondary,
},
icon: {
primary: theme.palette.icon?.primary ?? primary,
secondary: theme.palette.icon?.secondary ?? secondary,
},
text: {
primary: theme.palette.text?.primary ?? primary,
secondary: theme.palette.text?.secondary ?? secondary,
tertiary: theme.palette.text?.tertiary ?? `${primary}, 0.34`,
},
}
return palette
}
export const createTheme = (
props: CreateThemeProps,

View File

@ -15,15 +15,8 @@ const darkTheme = createTheme(
breakpoints: {},
typography: {},
palette: {
background: {
primary: '#000',
},
surface: {
primary: '#fff',
},
text: {
primary: 'rgb(255, 255, 255)',
},
primary: '255, 255, 255',
secondary: '0, 0, 0',
},
},
lightTheme,

View File

@ -1,109 +1,104 @@
import { css } from '@emotion/react'
import {
LSD_NAMESPACE,
THEME_BREAKPOINTS,
THEME_TYPOGRAPHY_PROPERTIES,
THEME_TYPOGRAPHY_VARIANTS,
} from './constants'
import { Breakpoints, Theme, TypographyVariants } from './types'
export const gs = {
breakpoint: (
theme: Theme,
breakpoint: Breakpoints,
content: string,
) => `@media (min-width: ${theme.breakpoints[breakpoint].width}px) {
${content}
}`,
import { Theme, TypographyProperties, TypographyVariants } from './types'
import { withTheme } from './withTheme'
const cssUtils = {
vars: {
name: (...parts: string[]) => `--${[LSD_NAMESPACE, ...parts].join('-')}`,
wrap: (v: string, wrap = true) => (!wrap ? v : `var(${v})`),
define: (name: string, value: any) => `${name}: ${value};`,
lsd: (...seq: string[]) => `--${['lsd', ...seq].join('-')}`,
typography: (
variant: TypographyVariants,
property: string,
breakpoint?: Breakpoints | 'default',
) => gs.vars.name(variant, property, ...(breakpoint ? [breakpoint] : [])),
palette: (category: string, variant: string) =>
gs.vars.name(category, variant),
variant: TypographyVariants | string,
property: TypographyProperties | string,
) => cssUtils.vars.lsd(variant, property),
color: (category: string, variant: string) =>
cssUtils.vars.lsd(category, variant),
wrap: (name: string) => `var(${name})`,
},
all: (theme: Theme) =>
[gs.typography.all(theme), gs.palette.all(theme)].join('\n'),
define: (name: string, value: string) => `${name}: ${value};`,
}
typography: {
all: (theme: Theme) => [gs.typography.variants(theme)].join('\n'),
const generateThemeGlobalStyles = withTheme((theme) => {
const vars: Array<string | string[]> = []
const styles: Array<string | string[]> = []
const breakpointStyles: string[][] = THEME_BREAKPOINTS.map(() => [])
const breakpointVars: string[][] = THEME_BREAKPOINTS.map(() => [])
variants: (theme: Theme) =>
[
...THEME_TYPOGRAPHY_VARIANTS.flatMap((variant) =>
THEME_TYPOGRAPHY_PROPERTIES.map((prop) =>
gs.vars.define(
gs.vars.typography(variant, prop),
theme.typography[variant][prop],
),
),
),
gs.typography.breakpoints(theme),
].join('\n'),
{
THEME_TYPOGRAPHY_VARIANTS.forEach((variant) => {
THEME_TYPOGRAPHY_PROPERTIES.forEach((property) => {
const value = theme.typography[variant][property]?.toString() ?? 'unset'
vars.push(
cssUtils.define(cssUtils.vars.typography(variant, property), value),
)
})
})
breakpoints: (theme: Theme) =>
THEME_BREAKPOINTS.map((breakpoint, index) =>
gs.breakpoint(
theme,
breakpoint,
gs.typography.breakpoint(theme, breakpoint, index),
),
).join('\n'),
THEME_BREAKPOINTS.forEach((breakpoint, breakpointIndex) => {
THEME_TYPOGRAPHY_VARIANTS.forEach((variant) => {
THEME_TYPOGRAPHY_PROPERTIES.forEach((property) => {
const value =
theme.breakpoints[breakpoint].typography[variant][property]
breakpoint: (
theme: Theme,
breakpoint: Breakpoints,
breakpointIndex: number,
) =>
THEME_TYPOGRAPHY_VARIANTS.flatMap((variant) =>
THEME_TYPOGRAPHY_PROPERTIES.map((prop) => {
const value = theme.breakpoints[breakpoint].typography[variant][prop]
const current =
breakpointIndex > 0
? theme.breakpoints?.[THEME_BREAKPOINTS[breakpointIndex - 1]]
?.typography?.[variant]?.[prop]
: theme.typography[variant][prop]
?.typography?.[variant]?.[property]
: theme.typography[variant][property]
return value !== current
? gs.vars.define(gs.vars.typography(variant, prop), value)
: undefined
}),
)
.filter((value) => !!value)
.join('\n'),
},
if (value && value !== current) {
breakpointVars[breakpointIndex].push(
cssUtils.define(
cssUtils.vars.typography(variant, property),
value.toString(),
),
)
}
})
})
})
}
palette: {
all: (theme: Theme) => {
const palette = theme.palette as Record<string, Record<string, string>>
{
const { primary, secondary, ...rest } = theme.palette
const palette = rest as Record<string, Record<string, string>>
return [
...Object.keys(palette).flatMap((name) =>
Object.keys(palette[name]).map((variant) =>
gs.palette.color(name, variant, palette[name][variant]),
vars.push(
cssUtils.define(cssUtils.vars.color('theme', 'primary'), primary),
cssUtils.define(cssUtils.vars.color('theme', 'secondary'), secondary),
...Object.keys(palette).flatMap((name) =>
Object.keys(palette[name]).map((variant) =>
cssUtils.define(
cssUtils.vars.color(name, variant),
palette[name][variant],
),
),
),
)
}
`:root {
html, body {
background-color: ${theme.palette.background.primary};
}
THEME_BREAKPOINTS.map((breakpoint, index) => {
styles.push(`@media (min-width: ${theme.breakpoints[breakpoint].width}px) {
:root {
${breakpointVars[index]}
}
`,
].join('\n')
},
color: (name: string, variant: string, value: string) =>
gs.vars.define(gs.vars.palette(name, variant), value),
},
}
${breakpointStyles[index]}
}`)
})
return css`
:root {
${vars}
}
${styles}
`
})
export const createThemeGlobalStyles = (() => {
return (theme: Theme) => {
@ -117,15 +112,8 @@ export const createThemeGlobalStyles = (() => {
)
return cache[key]
const styles = globalStyles.all(theme)
cache[key] = css`
:root {
${styles}
}
`
cache[key] = generateThemeGlobalStyles(theme)
return cache[key]
}
})()
export const globalStyles = gs

View File

@ -7,7 +7,7 @@ export {
} from './constants'
export { createTheme } from './createTheme'
export { defaultThemes } from './defaultThemes'
export { createThemeGlobalStyles, globalStyles } from './globalStyles'
export { createThemeGlobalStyles } from './globalStyles'
export { ThemeProvider, type ThemeProviderProps } from './ThemeProvider'
export * from './types'
export { useTheme } from './useTheme'

View File

@ -5,19 +5,21 @@ import { DeepPartial } from 'utility-types'
export type Breakpoints = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
export type TypographyTypefaces = 'sans-serif' | 'serif' | 'mono'
export type TypographyVariants =
| 'headlineLg'
| 'headlineStd'
| 'headlineMd'
| 'headlineSm'
| 'titleLg'
| 'titleMd'
| 'titleSm'
| 'bodyLg'
| 'bodyMd'
| 'bodySm'
| 'labelLg'
| 'labelMd'
| 'labelSm'
| 'display1'
| 'display2'
| 'h1'
| 'h2'
| 'h3'
| 'h4'
| 'h5'
| 'h6'
| 'subtitle1'
| 'subtitle2'
| 'body1'
| 'body2'
| 'body3'
| 'label1'
| 'label2'
export type VariantThemeProperties = keyof Pick<Theme, 'typography'>
@ -28,30 +30,25 @@ export type ThemeTypography<T extends string = TypographyVariants> = {
}
export type ThemePalette = {
background: {
primary: string
secondary: string
surface: {
primary: string
secondary: string
}
border: {
primary: string
secondary: string
tertiary: string
}
surface: {
primary: string
secondary: string
tertiary: string
disabled: string
}
text: {
primary: string
secondary: string
placeholder: string
disabled: string
tertiary: string
}
icons: {
icon: {
primary: string
disabled: string
secondary: string
}
}