with React
@TejasKumar_
ReactJS Developer
@kh_mugh
Design Systems
@kh_mugh
Implementation
@kh_mugh
@kh_mugh
@kh_mugh
If it affects color, it’s related to theming. Otherwise, it’s not.
// most VIKING way
<h1 className={props.theme === 'DARK' ? 'DARK-color' : 'LIGHT-color'}>ReactConf Rocks</h1>
// we can do better
<h1 className={`${props.theme}-color`}>ReactConf Rocks</h1>
// maybe this too 🤔
<h1 className={getClassName(props.theme, 'color')}>ReactConf Rocks</h1>
@media (prefers-color-scheme: light) { // light, dark and no-preference
html {
// light or dark styles
}
}This all works, until we add styling complexity
Component
Themes
Styles
Page
DEMO
Related only to the component
Only changes color theme
Changes "appearance" of shapes (i.e. borders, shadows, gradients etc.)
Related to the page
Can affect component themes
pageComponents.map((comp, idx) => {
const Component = COMPONENTS[comp.id]
return (
<ThemeProvider
key={idx}
theme={{
palette,
pageStyleName,
componentThemeName: comp.themeName
}}
>
<Component data={comp.data} />
</ThemeProvider>
)
})
const Container = styled.div`
background: ${props => {
if(props.theme.componentThemeName === 'DARK') {
if(props.theme.pageStyleName === 'ROUND') {
return `
...OVERRIDE SOME STYLES
`
}
return `
...DARK STYLES
`
}
...ALL OTHER THEMES
}};
`index.js // interface file
Component.js // actual component UI code
Component.meta.js // meta information about components validations, default props etc.
Component.theme.js // THEMEEEE
Component.styled.js // CSS-in-JS with Theme usage
Component.reducer.js // component reducer
Component.actions.js // component actions
Component.styles.sass // static styles for components
// will be added
Component.spec.js // UI test file
Component.stories.js // storybook file// Component.theme.js
import { hexToRgb } from '../../core/lib/utils'
export default {
DARK: {
border: () => 'none',
shadow: () => 'none',
buttonShadow: () => 'none',
raisedShadow: () => 'none',
heading: p => p.accentWhite,
paragraph: p => p.accentWhite,
background: p => `rgba(${hexToRgb(p.tertiaryDark)}, 1)`,
lightBackground: p => p.tertiaryExtraLight
},
LIGHT: {
heading: p => p.tertiaryDark,
paragraph: p => p.tertiaryNormal,
background: p => `rgba(${hexToRgb(p.tertiaryLight)}, 1)`,
lightBackground: p => p.tertiaryExtraLight
},
EXTRALIGHT: {
heading: p => p.tertiaryDark,
paragraph: p => p.tertiaryNormal,
background: p => `rgba(${hexToRgb(p.tertiaryExtraLight)}, 1)`,
lightBackground: p => p.tertiaryExtraLight
}
}
// Component.styled.js
import styled from '@emotion/styled' or 'styled-components'
import THEME from './Component.theme'
import { styleProvider } from '../../core/lib/utils'
const getStyleForProp = styleProvider(THEME)
const Container = styled.div`
padding: 80px 0;
background-color: ${getStyleForProp('background')};
`
const Title = styled.h4`
color: ${getStyleForProp('heading')};
`
const Description = styled.p`
color: ${getStyleForProp('paragraph')};
line-height: 22px;
text-align: left;
margin-bottom: 30px;
@media (max-width: 992px) {
text-align: center;
}
`
const Card = styled.div`
box-shadow: ${getStyleForProp('raisedShadow')};
border: ${getStyleForProp('border')};
border-radius: ${getStyleForProp('borderRadius')};
`
Now we have defined theme, time for defining the page styles
Page styles differ from themes with only `keys` of style names
// page.styles.js
export default {
ROUNDED: {
borderRadius: () => `10px`,
cardRadius: () => `10px`,
...otherRoundedStyles,
},
CORNERED: {
borderRadius: () => `5px`,
cardRadius: () => `5px`,
...otherCorneredStyles,
},
SHARP: {
border: p => `1px solid ${p.primaryDark}`,
borderRadius: () => `none`,
cardRadius: () => `none`,
...otherSharpStyles,
}
}
// Component.styled.js
import PAGE_STYLES from '../page.styles.js'
const getStyleForProp = styleProvider(THEME)
const Container = styled.div`
background-color: ${getStyleForProp('background')};
`
// styleProvider.js
// themeData - THEME
// styleProp - 'background'
// { componentTheme } - component props which are auto curried
const styleProvider = themeData => styleProp => componentProps => {
const { theme } = componentProps
// get selected theme name for component, and selected page style name
const { componentThemeName, pageStyleName, palette } = theme
const stylesFromTheme = themeData[componentThemeName] // componentThemeName - DARK, LIGHT or EXTRALIGHT
const stylesFromPage = PAGE_STYLES[pageStyleName] // pageStyleName - ROUND, CORNDERED or SHARP
const styles = {
...stylesFromPage,
...stylesFromTheme,
}
const styleGetter = styles[styleProp]
return styleGetter ? styleGetter(palette) : ''
}The best part is that we can override global page styles from any theme
{
// from global page styles
borderRadius: () => `10px`,
buttonRadius: () => `10px`,
inputRadius: () => `10px`,
buttonBackground: (p: Palette) =>
`linear-gradient(-180deg, ${p.primaryLight} 0%, ${p.primaryDark} 96%)`,
buttonBackgroundBlue: (p: Palette) =>
`linear-gradient(-180deg, ${p.secondaryLight} 0%, ${
p.secondaryDark
} 96%)`,
buttonBackgroundWhite: (p: Palette) =>
`linear-gradient(-180deg, ${p.tertiaryExtraLight} 0%, ${
p.tertiaryLight
} 96%)`,
buttonShadow: p => `0 3px 16px 0 rgba(${hexToRgb(p.primaryDark)}, 0.5)`,
buttonShadowBlue: p =>
`0 3px 16px 0 rgba(${hexToRgb(p.secondaryDark)}, 0.5)`,
shadow: (p: Palette) =>
`0 3px 10px 0 rgba(${hexToRgb(p.tertiaryDark)},0.08)`,
raisedShadow: (p: Palette) =>
`0 9px 22px 0 rgba(${hexToRgb(p.tertiaryDark)},0.12)`,
// from component theme
shadow: () => 'none',
buttonShadow: () => 'none',
raisedShadow: () => 'none',
heading: p => p.accentWhite,
paragraph: p => p.accentWhite,
background: p => `rgba(${hexToRgb(p.tertiaryDark)}, 1)`,
lightBackground: p => p.tertiaryExtraLight
}Brent Jackson
@jxnblk
Wrap component with according theme context
Declare it's own theme file for every component with styles that will be affected from theme and page styles
Create page styles object with global styles
The main focus was to create a system with minimal and descriptive API's so our UI engineers can work on that without large learning curve
🤔
Alternative ways
Basically, we could have achieved all this with CSS variables
Or when using Emtotion's `css` utility leverage the same thing only with classNames, not components
(theme is visible via callback)
Framework/Library agnostic
Thank you!
@kh_mugh