Skip to content

Commit

Permalink
theming with theme object
Browse files Browse the repository at this point in the history
  • Loading branch information
SamueleA committed Oct 17, 2023
1 parent f084fcb commit 8ffa718
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 149 deletions.
File renamed without changes.
3 changes: 2 additions & 1 deletion src/components/ThemeProvider/ThemeProvider.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ export default {

export const Default = () => {
const { theme, setTheme } = useTheme()

const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light')
setTheme(theme)
}

return <Button label="Toggle theme" onClick={toggleTheme} />
Expand Down
69 changes: 40 additions & 29 deletions src/components/ThemeProvider/ThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,36 @@ import {
useState,
} from 'react'

const THEMES = ['dark', 'light'] as const
export interface Color {
r: number
g: number
b: number
}

export type Theme = (typeof THEMES)[number]
export interface Theme {
foreground: Color
background: Color
backgroundBackdrop: Color,
backgroundRaised: Color,
statusPositive: Color
statusNegative: Color
statusWarning: Color
statusInfo: Color
primaryButton: string
}

const DEFAULT_THEME: Theme = {
foreground: { r: 255, g: 255, b: 255 },
background: { r: 0, g: 0, b: 0 },
backgroundBackdrop: { r: 34, g: 34, b: 34 },
backgroundRaised: { r: 54, g: 54, b: 54 },
statusPositive: { r: 31, g: 194, b: 102 },
statusNegative: { r: 194, g: 80, b: 31 },
statusWarning: { r: 244, g: 176, b: 62 },
statusInfo: { r: 0, g: 118, b: 204 },
primaryButton: 'linear-gradient(89.69deg, #4411E1 0.27%, #7537F9 99.73%)'
}

const DEFAULT_THEME = 'dark'
const THEME_ATTR = 'data-theme'
const STORAGE_KEY = '@sequence.theme'

interface ThemeContextValue {
Expand All @@ -25,18 +49,13 @@ interface ThemeProviderProps {
}

const getTheme = (): Theme => {
const persistedTheme = localStorage.getItem(STORAGE_KEY) as Theme
const persistedThemeRaw = localStorage.getItem(STORAGE_KEY)

if (THEMES.includes(persistedTheme)) {
if (persistedThemeRaw) {
const persistedTheme = JSON.parse(persistedThemeRaw) as Theme
return persistedTheme
}

// else if (matchMedia(`(prefers-color-scheme: light)`).matches) {
// return 'light'
// } else if (matchMedia(`(prefers-color-scheme: dark)`).matches) {
// return 'dark'
// }

return DEFAULT_THEME
}

Expand All @@ -45,6 +64,7 @@ const ThemeContext = createContext<ThemeContextValue | null>(null)
export const ThemeProvider = (props: PropsWithChildren<ThemeProviderProps>) => {
const [theme, setTheme] = useState<Theme>(props.theme || getTheme())


useEffect(() => {
// Add is-apple class
;/Mac/.test(window.navigator.userAgent) &&
Expand All @@ -53,32 +73,23 @@ export const ThemeProvider = (props: PropsWithChildren<ThemeProviderProps>) => {

// Allow prop theme override
useEffect(() => {
if (props.theme && THEMES.includes(props.theme)) {
if (props.theme) {
setTheme(props.theme)
}
}, [props.theme])

// Set the data-theme attribtute on the document root element
useEffect(() => {
const root = document.querySelector(':root')

if (root) {
root.setAttribute(THEME_ATTR, theme)
}
}, [theme])

// Create the context value
const value: ThemeContextValue = useMemo(() => {
return {
theme,
setTheme: (mode: Theme) => {
if (THEMES.includes(mode)) {
// Save to local storage
localStorage.setItem(STORAGE_KEY, mode)

// Set the theme state which will cause a re-render
setTheme(mode)
}
setTheme: (newTheme: Theme) => {
const themeString = JSON.stringify(newTheme)
// Save to local storage
localStorage.setItem(STORAGE_KEY, themeString)

// Set the theme state which will cause a re-render
setTheme(newTheme)
},
}
}, [theme])
Expand Down
1 change: 1 addition & 0 deletions src/components/ThemeProvider/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { ThemeProvider, useTheme } from './ThemeProvider'
export type { Theme, Color } from './ThemeProvider'
10 changes: 5 additions & 5 deletions src/css/vars.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import {
createGlobalThemeContract,
} from '@vanilla-extract/css'

import { Theme } from '~/components/ThemeProvider'
import { capitalize } from '~/helpers'
import { ColorScheme, tokens } from '~/tokens'
import { getColors, getTokens, NetworkColors } from '~/tokens'

import { mapVarName } from './utils'
import { getColorSchemes } from '~/tokens/color'

type MapTokens<P extends string, T> = {
[K in keyof T & string as `${P}${Capitalize<K>}`]: string
Expand All @@ -21,8 +23,6 @@ const mapTokens = <P extends string, T extends {}>(
}, {}) as MapTokens<P, T>
}

type NetworkColors = typeof tokens.colors.network

const mapNetworkColors = <
T extends NetworkColors,
K extends keyof NetworkColors,
Expand All @@ -43,8 +43,8 @@ export const baseVars = createGlobalThemeContract(baseTokens, mapVarName)

createGlobalTheme(':root', baseVars, baseTokens)

const makeColorScheme = (mode: ColorScheme = 'light') => {
const colorSchemeTokens = colors.colorSchemes[mode]
const makeColorScheme = (theme: Theme) => {
const colorSchemeTokens = getColorSchemes(theme)

return {
colors: {
Expand Down
171 changes: 73 additions & 98 deletions src/tokens/color.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Theme, Color } from '~/components/ThemeProvider'

export type NetworkColors = typeof networkColors

export interface ColorTokens {
base: BaseColors
context: ContextColors
network: typeof networkColors
network: NetworkColors
gradient: Gradients
colorSchemes: ColorSchemes
colorSchemes: ColorSchemeTokens
}

export type ColorScheme = 'dark' | 'light'

interface ColorSchemeTokens {
background: BackgroundColors
border: BorderColors
Expand All @@ -17,8 +19,6 @@ interface ColorSchemeTokens {
// context: ContextColors // ContextColors are global and not color scheme specific
}

type ColorSchemes<T = ColorSchemeTokens> = { [key in ColorScheme]: T }

interface BaseColors {
black: string
white: string
Expand Down Expand Up @@ -69,68 +69,46 @@ interface Gradients {
secondary: string
}

const backgroundColors: ColorSchemes<BackgroundColors> = {
dark: {
primary: 'rgba(0, 0, 0, 1)',
secondary: 'rgba(255, 255, 255, 0.1)',
contrast: 'rgba(0, 0, 0, 0.5)',
muted: 'rgba(255, 255, 255, 0.05)',
control: 'rgba(255, 255, 255, 0.25)',
inverse: 'rgba(255, 255, 255, 1)',
backdrop: 'rgba(34, 34, 34, 0.9)',
overlay: 'rgba(0, 0, 0, 0.7)',
raised: 'rgba(54, 54, 54, 0.7)',
},
light: {
primary: 'rgba(244, 244, 244, 1)',
secondary: 'rgba(0, 0, 0, 0.1)',
contrast: 'rgba(244, 244, 244, 0.5)',
muted: 'rgba(0, 0, 0, 0.05)',
control: 'rgba(0, 0, 0, 0.25)',
inverse: 'rgba(0, 0, 0, 1)',
backdrop: 'rgba(221, 221, 221, 0.9)',
overlay: 'rgba(244, 244, 244, 0.7)',
raised: 'rgba(192, 192, 192, 0.7)',
},
const makeRGBA = (color: Color, alpha: number) => {
return `rgba(${color.r},${color.g},${color.b},${alpha})`
}

const borderColors: ColorSchemes<BorderColors> = {
dark: {
normal: 'rgba(255, 255, 255, 0.25)',
focus: 'rgba(255, 255, 255, 0.5)',
},
light: {
normal: 'rgba(0, 0, 0, 0.25)',
focus: 'rgba(0, 0, 0, 0.5)',
},
const getBackgroundColors = (theme: Theme): BackgroundColors => {
return ({
primary: makeRGBA(theme.background, 1),
secondary: makeRGBA(theme.foreground, 1),
contrast: makeRGBA(theme.background, 0.5),
muted: makeRGBA(theme.foreground, 0.05),
control: makeRGBA(theme.foreground, 0.25),
inverse: makeRGBA(theme.foreground, 1),
backdrop: makeRGBA(theme.backgroundBackdrop, 0.9),
overlay: makeRGBA(theme.background, 0.7),
raised: makeRGBA(theme.backgroundRaised, 0.7),
})
}

const buttonColors: ColorSchemes<ButtonColors> = {
dark: {
glass: 'rgba(255, 255, 255, 0.15)',
emphasis: 'rgba(0, 0, 0, 0.5)',
inverse: 'rgba(255, 255, 255, 0.8)',
},
light: {
glass: 'rgba(0, 0, 0, 0.15)',
emphasis: 'rgba(255, 255, 255, 0.5)',
inverse: 'rgba(0, 0, 0, 0.8)',
},
const getBorderColors = (theme: Theme): BorderColors => {
return ({
normal: makeRGBA(theme.foreground, 0.25),
focus: makeRGBA(theme.foreground, 0.5),
})
}

const textColors: ColorSchemes<TextColors> = {
dark: {
'100': 'rgba(255, 255, 255, 1)',
'80': 'rgba(255, 255, 255, 0.8)',
'50': 'rgba(255, 255, 255, 0.5)',
inverse100: 'rgba(0, 0, 0, 1)',
},
light: {
'100': 'rgba(0, 0, 0, 1)',
'80': 'rgba(0, 0, 0, 0.8)',
'50': 'rgba(0, 0, 0, 0.5)',
inverse100: 'rgba(255, 255, 255, 1)',
},
const getButtonColors = (theme: Theme): ButtonColors => {
return ({
glass: makeRGBA(theme.foreground, 0.15),
emphasis: makeRGBA(theme.background, 0.5),
inverse: makeRGBA(theme.foreground, 0.8),
})
}

const getTextColors = (theme: Theme): TextColors => {
return ({
'100': makeRGBA(theme.foreground, 1),
'80': makeRGBA(theme.foreground, 0.8),
'50': makeRGBA(theme.foreground, 0.5),
inverse100: makeRGBA(theme.background, 1),
})
}

// ContextColors are global and not color scheme specific
Expand All @@ -141,16 +119,17 @@ const contextColors: ContextColors = {
warning: '#F4B03E',
}

// Gradients are global and not color scheme specific
const gradients: Gradients = {
backdrop: `linear-gradient(
243.18deg,
rgba(86, 52, 189, 0.85) 0%,
rgba(49, 41, 223, 0.85) 63.54%,
rgba(7, 98, 149, 0.85) 100%
)`,
primary: `linear-gradient(89.69deg, #4411E1 0.27%, #7537F9 99.73%)`,
secondary: `linear-gradient(32.51deg, #951990 -15.23%, #3A35B1 48.55%, #20A8B0 100%)`,
const getGradients = (theme: Theme): Gradients => {
return ({
backdrop: `linear-gradient(
243.18deg,
rgba(86, 52, 189, 0.85) 0%,
rgba(49, 41, 223, 0.85) 63.54%,
rgba(7, 98, 149, 0.85) 100%
)`,
primary: theme.primaryButton,
secondary: `linear-gradient(32.51deg, #951990 -15.23%, #3A35B1 48.55%, #20A8B0 100%)`,
})
}

const networkColors = {
Expand Down Expand Up @@ -180,30 +159,26 @@ const networkColors = {
},
}

const colorSchemes: ColorSchemes = {
dark: {
background: backgroundColors.dark,
border: borderColors.dark,
button: buttonColors.dark,
text: textColors.dark,
},
light: {
background: backgroundColors.light,
border: borderColors.light,
button: buttonColors.light,
text: textColors.light,
},
}

export const colors: ColorTokens = {
base: {
black: '#000000',
white: '#ffffff',
inherit: 'inherit',
transparent: 'transparent',
},
context: contextColors,
gradient: gradients,
network: networkColors,
colorSchemes,
export const getColorSchemes = (theme: Theme): ColorSchemeTokens => {
return ({
background: getBackgroundColors(theme),
border: getBorderColors(theme),
button: getButtonColors(theme),
text: getTextColors(theme),
})
}

export const getColors = (theme: Theme): ColorTokens => {
return ({
base: {
black: '#000000',
white: '#ffffff',
inherit: 'inherit',
transparent: 'transparent',
},
context: contextColors,
gradient: getGradients(theme),
network: networkColors,
colorSchemes: getColorSchemes(theme),
})
}
Loading

0 comments on commit 8ffa718

Please sign in to comment.