From 17fce5417aee13d70d4908e37e045ecd37121765 Mon Sep 17 00:00:00 2001 From: moo-onthelawn <70078372+moo-onthelawn@users.noreply.github.com> Date: Wed, 24 Jan 2024 17:03:44 -0500 Subject: [PATCH] TRCL-3476 Create colorTokens file + new theme color types (#239) * introduce tokens file + theme type * fix lint error * TRCL-3497 Use color tokens in app + tradingView widget (#240) * update to use tokens, small fixes * fix eol lint * move usage style helper to lib/styles * move files, fix text colors * fix logo in light mode * remove colors.css file * small lint things * add transparency to button + destructive borders --- .ladle/components.tsx | 27 ++- src/App.tsx | 74 +++---- src/components/MarginUsageRing.tsx | 2 +- src/components/Switch.tsx | 2 +- src/components/UsageBars.tsx | 4 +- src/components/WithTooltip.tsx | 7 +- src/components/visx/TooltipContent.tsx | 7 +- src/constants/styles/base.ts | 70 +++++++ src/constants/styles/colors.ts | 77 ++++++++ src/hooks/tradingView/useTradingViewTheme.ts | 3 +- src/hooks/useAppTheme.tsx | 16 ++ src/icons/logo-short.tsx | 93 ++++----- src/index.css | 1 - src/lib/styles.ts | 20 ++ src/lib/tradingView/utils.ts | 44 ++--- src/pages/Profile.tsx | 4 +- src/styles/colors.css | 180 ------------------ src/styles/colors.ts | 125 ------------ src/styles/formMixins.ts | 2 +- src/styles/globalStyle.ts | 42 ++++ src/styles/themes.ts | 157 +++++++++++++++ src/views/forms/TradeForm/TradeSideToggle.tsx | 3 +- 22 files changed, 523 insertions(+), 437 deletions(-) create mode 100644 src/constants/styles/base.ts create mode 100644 src/constants/styles/colors.ts create mode 100644 src/hooks/useAppTheme.tsx create mode 100644 src/lib/styles.ts delete mode 100644 src/styles/colors.css delete mode 100644 src/styles/colors.ts create mode 100644 src/styles/globalStyle.ts create mode 100644 src/styles/themes.ts diff --git a/.ladle/components.tsx b/.ladle/components.tsx index 40cade6..04ee536 100644 --- a/.ladle/components.tsx +++ b/.ladle/components.tsx @@ -5,29 +5,35 @@ import styled from 'styled-components'; import { store } from '@/state/_store'; +import { GlobalStyle } from '@/styles/globalStyle'; + import { SelectMenu, SelectItem } from '@/components/SelectMenu'; +import { AppThemeProvider } from '@/hooks/useAppTheme'; + +import { AppTheme, setAppTheme } from '@/state/configs'; import { setLocaleLoaded } from '@/state/localization'; import '@/index.css'; import './ladle.css'; export const StoryWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const [theme, setTheme] = useState('Default theme'); + const [theme, setTheme] = useState(AppTheme.Classic); useEffect(() => { + store.dispatch(setAppTheme(theme)); switch (theme) { - case 'Dark theme': { + case AppTheme.Dark: { document?.documentElement?.classList.remove('theme-light'); document?.documentElement?.classList.add('theme-dark'); break; } - case 'Light theme': { + case AppTheme.Light: { document?.documentElement?.classList.remove('theme-dark'); document?.documentElement?.classList.add('theme-light'); break; } - default: { + case AppTheme.Classic: { document?.documentElement?.classList.remove('theme-dark', 'theme-light'); break; } @@ -48,15 +54,15 @@ export const StoryWrapper: React.FC<{ children: React.ReactNode }> = ({ children > {[ { - value: 'Default theme', + value: AppTheme.Classic, label: 'Default theme', }, { - value: 'Dark theme', + value: AppTheme.Dark, label: 'Dark theme', }, { - value: 'Light theme', + value: AppTheme.Light, label: 'Light theme', }, ].map(({ value, label }) => ( @@ -69,8 +75,11 @@ export const StoryWrapper: React.FC<{ children: React.ReactNode }> = ({ children
- {children} - + + + {children} + + ); }; diff --git a/src/App.tsx b/src/App.tsx index ae59c05..416b110 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,6 +16,7 @@ import { } from '@/hooks'; import { DydxProvider } from '@/hooks/useDydxClient'; import { AccountsProvider } from '@/hooks/useAccounts'; +import { AppThemeProvider } from '@/hooks/useAppTheme'; import { DialogAreaProvider, useDialogArea } from '@/hooks/useDialogArea'; import { LocaleProvider } from '@/hooks/useLocaleSeparators'; import { NotificationsProvider } from '@/hooks/useNotifications'; @@ -35,6 +36,7 @@ import { GlobalCommandDialog } from '@/views/dialogs/GlobalCommandDialog'; import { config } from '@/lib/wagmi'; import { breakpoints } from '@/styles'; +import { GlobalStyle } from '@/styles/globalStyle'; import { layoutMixins } from '@/styles/layoutMixins'; import { LoadingSpace } from './components/Loading/LoadingSpinner'; @@ -66,49 +68,52 @@ const Content = () => { const { chainTokenLabel } = useTokenConfigs(); return ( - - {isNotTablet && } + <> + + + {isNotTablet && } - - }> - - - } /> - } /> - + + }> + + + } /> + } /> + - } /> - } /> - {isTablet && ( - <> - } /> - } /> - } /> - - )} + } /> + } /> + {isTablet && ( + <> + } /> + } /> + } /> + + )} - }> - } /> - + }> + } /> + - } /> - } /> + } /> + } /> - } /> - - - + } /> + + + - {isTablet ? : } + {isTablet ? : } - + - - - + + + - - + + + ); }; @@ -131,6 +136,7 @@ const providers = [ wrapProvider(LocalNotificationsProvider), wrapProvider(NotificationsProvider), wrapProvider(DialogAreaProvider), + wrapProvider(AppThemeProvider), ]; const App = () => { diff --git a/src/components/MarginUsageRing.tsx b/src/components/MarginUsageRing.tsx index 51483f1..60c1e6b 100644 --- a/src/components/MarginUsageRing.tsx +++ b/src/components/MarginUsageRing.tsx @@ -1,11 +1,11 @@ import styled, { type AnyStyledComponent } from 'styled-components'; import { RiskLevels } from '@/constants/abacus'; -import { UsageColorFromRiskLevel } from '@/styles/colors'; import { Ring } from '@/components/Ring'; import { abacusHelper } from '@/lib/abacus'; +import { UsageColorFromRiskLevel } from '@/lib/styles'; type ElementProps = { value: number; diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx index 103f933..cc8b149 100644 --- a/src/components/Switch.tsx +++ b/src/components/Switch.tsx @@ -47,7 +47,7 @@ Styled.Root = styled(Root)` --switch-thumb-backgroundColor: var(--color-layer-6); --switch-active-backgroundColor: var(--color-accent); - --switch-active-thumb-backgroundColor: var(--color-white); + --switch-active-thumb-backgroundColor: ${({ theme }) => theme.switchThumbActiveBackground}; position: relative; width: var(--switch-width); diff --git a/src/components/UsageBars.tsx b/src/components/UsageBars.tsx index 07322fb..3b77a17 100644 --- a/src/components/UsageBars.tsx +++ b/src/components/UsageBars.tsx @@ -2,9 +2,9 @@ import styled, { type AnyStyledComponent } from 'styled-components'; import { type RiskLevels } from '@/constants/abacus'; -import { UsageColorFromRiskLevel } from '@/styles/colors'; - import { abacusHelper } from '@/lib/abacus'; +import { UsageColorFromRiskLevel } from '@/lib/styles'; + type ElementProps = { value: number; }; diff --git a/src/components/WithTooltip.tsx b/src/components/WithTooltip.tsx index 34b0f14..a817f3a 100644 --- a/src/components/WithTooltip.tsx +++ b/src/components/WithTooltip.tsx @@ -110,12 +110,7 @@ Styled.Abbr = styled.abbr` Styled.Content = styled(Content)` --tooltip-backgroundColor: var(--color-layer-4); - --tooltip-backgroundColor: hsl( - var(--layer-base-hue), - var(--layer-base-saturation), - calc(var(--layer-base-lightness) + 4%), - 0.66 - ); + --tooltip-backgroundColor: ${({ theme }) => theme.tooltipBackground}; ${popoverMixins.popover} --popover-backgroundColor: var(--tooltip-backgroundColor); diff --git a/src/components/visx/TooltipContent.tsx b/src/components/visx/TooltipContent.tsx index db3d108..7b594f1 100644 --- a/src/components/visx/TooltipContent.tsx +++ b/src/components/visx/TooltipContent.tsx @@ -27,12 +27,7 @@ Styled.TooltipContent = styled.aside<{ accentColor?: string }>` ${popoverMixins.popover} --popover-radius: 0.5rem; - --popover-background-color: hsl( - var(--layer-base-hue), - var(--layer-base-saturation), - calc(var(--layer-base-lightness)), - 0.9 - ); + --popover-background-color: ${({ theme }) => theme.popoverBackground}; --popover-backdrop-filter: saturate(120%) blur(12px); display: grid; diff --git a/src/constants/styles/base.ts b/src/constants/styles/base.ts new file mode 100644 index 0000000..8251ffa --- /dev/null +++ b/src/constants/styles/base.ts @@ -0,0 +1,70 @@ +export enum ColorToken { + Black = '#000000', + White = '#FFFFFF', + + LightGray0 = '#FAFAFA', + LightGray1 = '#F7F8FA', + LightGray2 = '#FAFAFD', + LightGray3 = '#F5F5F5', + LightGray4 = '#EDF0F2', + LightGray5 = '#EDEDED', + LightGray6 = '#E9ECEE', + LightGray7 = '#E6E6E6', + LightGray8 = '#DBDBDB', + LightGray9 = '#D5DADF', + LightGray10 = '#D1D9E0', + + MediumGray0 = '#CACACE', + MediumGray1 = '#C2CBD3', + + DarkGray0 = '#888891', + DarkGray1 = '#737373', + DarkGray2 = '#56565C', + DarkGray3 = '#4B4B4B', + DarkGray4 = '#494950', + DarkGray5 = '#3B3B3F', + DarkGray6 = '#303033', + DarkGray7 = '#313135', + DarkGray8 = '#292929', + DarkGray9 = '#23232F', + DarkGray10 = '#212124', + DarkGray11 = '#18181B', + DarkGray12 = '#181818', + DarkGray13 = '#0C0C0D', + + GrayBlue0 = '#464659', + GrayBlue1 = '#38384D', + GrayBlue2 = '#303045', + GrayBlue3 = '#29293D', + GrayBlue4 = '#212131', + GrayBlue5 = '#181825', + GrayBlue6 = '#101018', + GrayBlue7 = '#08080C', + + GrayPurple0 = '#DFDADF', + GrayPurple1 = '#C8C7D8', + GrayPurple2 = '#807E98', + + Purple0 = '#7774FF', + Purple1 = '#6966FF', + + Green0 = '#1AFFB9', + Green1 = '#3ED9A4', + Green2 = '#2CCC98', + Green3 = '#3EB68A', + + Yellow0 = '#FFCC48', + Yellow1 = '#FFB647', + + Red0 = '#FF5C5C', + Red1 = '#E76565', + Red2 = '#E45555', +} + +/** Maps opacity to corresponding hex value */ +export enum OpacityToken { + Opacity16 = '29', + Opacity20 = '33', + Opacity66 = 'A8', + Opacity90 = 'E6', +} diff --git a/src/constants/styles/colors.ts b/src/constants/styles/colors.ts new file mode 100644 index 0000000..a7c9eb1 --- /dev/null +++ b/src/constants/styles/colors.ts @@ -0,0 +1,77 @@ +export type ThemeColors = LayerColors & + BorderColors & + TextColors & + GradientColors & + AccentColors & + StatusColors & + DirectionalColors & + RiskColors & + IconColors & + ComponentColors; + +type LayerColors = { + layer0: string; + layer1: string; + layer2: string; + layer3: string; + layer4: string; + layer5: string; + layer6: string; + layer7: string; +}; + +type BorderColors = { + borderDefault: string; + borderDestructive: string; + borderButton: string; +}; + +type TextColors = { + textPrimary: string; + textSecondary: string; + textTertiary: string; +}; + +type GradientColors = { + gradientBase0: string; + gradientBase1: string; +}; + +type AccentColors = { + accent: string; + accentFaded: string; + favorite: string; +}; + +type StatusColors = { + success: string; + warning: string; + error: string; +}; + +type DirectionalColors = { + positive: string; + negative: string; + positiveFaded: string; + negativeFaded: string; +}; + +type RiskColors = { + riskLow: string; + riskMedium: string; + riskHigh: string; +}; + +type IconColors = { + logoFill: string; + profileYellow: string; + profileRed: string; +}; + +type ComponentColors = { + inputBackground: string; + popoverBackground: string; + switchThumbActiveBackground: string; + toggleBackground: string; + tooltipBackground: string; +}; diff --git a/src/hooks/tradingView/useTradingViewTheme.ts b/src/hooks/tradingView/useTradingViewTheme.ts index 302c54f..6436063 100644 --- a/src/hooks/tradingView/useTradingViewTheme.ts +++ b/src/hooks/tradingView/useTradingViewTheme.ts @@ -4,7 +4,6 @@ import { useSelector } from 'react-redux'; import type { IChartingLibraryWidget, ThemeName } from 'public/tradingview/charting_library'; import { AppTheme } from '@/state/configs'; - import { getAppTheme } from '@/state/configsSelectors'; import { getWidgetOverrides } from '@/lib/tradingView/utils'; @@ -29,7 +28,7 @@ export const useTradingViewTheme = ({ tvWidget: (IChartingLibraryWidget & { _id?: string; _ready?: boolean }) | null; isWidgetReady?: boolean; }) => { - const appTheme = useSelector(getAppTheme); + const appTheme: AppTheme = useSelector(getAppTheme); useEffect(() => { if (tvWidget && isWidgetReady) { diff --git a/src/hooks/useAppTheme.tsx b/src/hooks/useAppTheme.tsx new file mode 100644 index 0000000..790cc89 --- /dev/null +++ b/src/hooks/useAppTheme.tsx @@ -0,0 +1,16 @@ +import { useSelector } from 'react-redux'; +import { ThemeProvider } from 'styled-components'; + +import { AppTheme } from '@/state/configs'; +import { getAppTheme } from '@/state/configsSelectors'; + +import { Themes } from '@/styles/themes'; + +export const AppThemeProvider = ({ ...props }) => { + return +}; + +export const useAppThemeContext = () => { + const theme: AppTheme = useSelector(getAppTheme); + return Themes[theme]; +} diff --git a/src/icons/logo-short.tsx b/src/icons/logo-short.tsx index c618ec4..81ffe2f 100644 --- a/src/icons/logo-short.tsx +++ b/src/icons/logo-short.tsx @@ -1,45 +1,52 @@ -const LogoShortIcon: React.FC<{ id?: string }> = ({ id }: { id?: string }) => ( - - - - - - - - - - - - - - - -); +import { useAppThemeContext } from '@/hooks/useAppTheme'; + +const LogoShortIcon: React.FC<{ id?: string }> = ({ id }: { id?: string }) => { + const theme = useAppThemeContext(); + const fill = theme.logoFill; + + return ( + + + + + + + + + + + + + + + + ); +}; export default LogoShortIcon; diff --git a/src/index.css b/src/index.css index 869904f..fc5919b 100644 --- a/src/index.css +++ b/src/index.css @@ -1,6 +1,5 @@ @import 'styles/fonts.css'; @import 'styles/text.css'; -@import 'styles/colors.css'; @import 'styles/animations.css'; :root { diff --git a/src/lib/styles.ts b/src/lib/styles.ts new file mode 100644 index 0000000..ede2dd4 --- /dev/null +++ b/src/lib/styles.ts @@ -0,0 +1,20 @@ +import { css } from 'styled-components'; + +import { type RiskLevels } from '@/constants/abacus'; + +export const UsageColorFromRiskLevel = (riskLevel: RiskLevels) => + ({ + low: css` + color: var(--color-risk-low); + `, + medium: css` + color: var(--color-risk-medium); + `, + high: css` + color: var(--color-risk-high); + `, + }[riskLevel.name]); + +export const generateFadedColorVariant = (colorHex: string, opacityHex: string) => { + return `${colorHex}${opacityHex}`; +}; diff --git a/src/lib/tradingView/utils.ts b/src/lib/tradingView/utils.ts index b4e77ab..5a50209 100644 --- a/src/lib/tradingView/utils.ts +++ b/src/lib/tradingView/utils.ts @@ -1,11 +1,9 @@ -import Color from 'color'; -import isEmpty from 'lodash/isEmpty'; - import { Candle, TradingViewBar, TradingViewSymbol } from '@/constants/candles'; -import { Colors } from '@/styles/colors'; import { AppTheme } from '@/state/configs'; +import { Themes } from '@/styles/themes'; + export const mapCandle = ({ startedAt, open, @@ -49,42 +47,42 @@ export const getHistorySlice = ({ return bars.filter(({ time }) => time >= fromMs); }; -export const getWidgetOverrides = (theme: AppTheme) => { - const colors = Colors[theme]; +export const getWidgetOverrides = (appTheme: AppTheme) => { + const theme = Themes[appTheme]; return { overrides: { - 'paneProperties.background': Color(colors.layer2).hex(), - 'paneProperties.horzGridProperties.color': Color(colors.layer3).hex(), - 'paneProperties.vertGridProperties.color': Color(colors.layer3).hex(), + 'paneProperties.background': theme.layer2, + 'paneProperties.horzGridProperties.color': theme.layer3, + 'paneProperties.vertGridProperties.color': theme.layer3, 'paneProperties.crossHairProperties.style': 1, 'paneProperties.legendProperties.showBarChange': false, 'paneProperties.backgroundType': 'solid', 'mainSeriesProperties.style': 1, - 'mainSeriesProperties.candleStyle.upColor': Color(colors.positive).hex(), - 'mainSeriesProperties.candleStyle.borderUpColor': Color(colors.positive).hex(), - 'mainSeriesProperties.candleStyle.wickUpColor': Color(colors.positive).hex(), - 'mainSeriesProperties.candleStyle.downColor': Color(colors.negative).hex(), - 'mainSeriesProperties.candleStyle.borderDownColor': Color(colors.negative).hex(), - 'mainSeriesProperties.candleStyle.wickDownColor': Color(colors.negative).hex(), + 'mainSeriesProperties.candleStyle.upColor': theme.positive, + 'mainSeriesProperties.candleStyle.borderUpColor': theme.positive, + 'mainSeriesProperties.candleStyle.wickUpColor': theme.positive, + 'mainSeriesProperties.candleStyle.downColor': theme.negative, + 'mainSeriesProperties.candleStyle.borderDownColor': theme.negative, + 'mainSeriesProperties.candleStyle.wickDownColor': theme.negative, 'mainSeriesProperties.statusViewStyle.symbolTextSource': 'ticker', - 'scalesProperties.textColor': Color(colors.text1).hex(), - 'scalesProperties.backgroundColor': Color(colors.layer2).hex(), - 'scalesProperties.lineColor': Color(colors.layer3).hex(), + 'scalesProperties.textColor': theme.textPrimary, + 'scalesProperties.backgroundColor': theme.layer2, + 'scalesProperties.lineColor': theme.layer3, }, studies_overrides: { - 'volume.volume.color.0': Color(colors.negative).hex(), - 'volume.volume.color.1': Color(colors.positive).hex(), + 'volume.volume.color.0': theme.negative, + 'volume.volume.color.1': theme.positive, 'volume.volume ma.visible': false, - 'relative strength index.plot.color': Color(colors.accent).hex(), + 'relative strength index.plot.color': theme.accent, 'relative strength index.plot.linewidth': 1.5, 'relative strength index.hlines background.color': '#134A9F', }, loading_screen: { - backgroundColor: Color(colors.layer2).hex(), - foregroundColor: Color(colors.layer2).hex(), + backgroundColor: theme.layer2, + foregroundColor: theme.layer2, }, }; }; diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index fc5703a..ff362bd 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -264,8 +264,8 @@ Styled.ProfileIcon = styled.div` border-radius: 50%; background: linear-gradient( 135deg, - var(--theme-classic-color-yellow) 0%, - var(--theme-classic-color-red) 100% + ${({ theme }) => theme.profileYellow} 0%, + ${({ theme }) => theme.profileRed} 100% ); `; diff --git a/src/styles/colors.css b/src/styles/colors.css deleted file mode 100644 index d3a6812..0000000 --- a/src/styles/colors.css +++ /dev/null @@ -1,180 +0,0 @@ -:root { - /* Parameters */ - - /* - --layer-base-hue - --layer-base-saturation - --layer-base-lightness - - --color-text-0 - --color-text-1 - --color-text-2 - - --color-accent - --color-positive - --color-negative - --color-success - --color-warning - --color-error - --color-favorite - */ - - /* Computed: Layers */ - - --color-layer-0: hsl( - var(--layer-base-hue), - var(--layer-base-saturation), - calc(var(--layer-base-lightness) - 12%) - ); - - --color-layer-1: hsl( - var(--layer-base-hue), - var(--layer-base-saturation), - calc(var(--layer-base-lightness) - 8%) - ); - - --color-layer-2: hsl( - var(--layer-base-hue), - var(--layer-base-saturation), - calc(var(--layer-base-lightness) - 4%) - ); - - --color-layer-3: hsl( - var(--layer-base-hue), - var(--layer-base-saturation), - var(--layer-base-lightness) - ); - - --color-layer-4: hsl( - var(--layer-base-hue), - var(--layer-base-saturation), - calc(var(--layer-base-lightness) + 4%) - ); - - --color-layer-5: hsl( - var(--layer-base-hue), - calc(var(--layer-base-saturation) - 2%), - calc(var(--layer-base-lightness) + 7%) - ); - - --color-layer-6: hsl( - var(--layer-base-hue), - calc(var(--layer-base-saturation) - 4%), - calc(var(--layer-base-lightness) + 10%) - ); - - --color-layer-7: hsla(var(--layer-base-hue), 0%, 100%, 0.07); - - --color-gradient-base-0: hsl( - var(--layer-base-hue), - calc(var(--layer-base-saturation) - 5%), - var(--layer-base-lightness) - ); - - --color-gradient-base-1: hsl( - var(--layer-base-hue), - calc(var(--layer-base-saturation) - 1%), - calc(var(--layer-base-lightness) + 7%) - ); - - /* Computed: Borders */ - - --color-border: var(--color-layer-5); - --color-border-white: hsla(0, 0%, 100%, 0.2); - --color-border-red: hsla(360, 73%, 61%, 0.2); -} - -/* Theme: Classic (default) */ -:root { - /* Constants */ - - --theme-classic-hue-purple: 240; - --theme-classic-color-green: hsl(159, 67%, 39%); - --theme-classic-color-yellow: hsl(36, 100%, 64%); - --theme-classic-color-red: hsl(360, 73%, 61%); - - /* Parameters */ - - --layer-base-hue: var(--theme-classic-hue-purple); - --layer-base-saturation: 20%; - --layer-base-lightness: 16%; - - --color-text-0: hsl(245, 11%, 55%); - --color-text-1: hsl(244, 18%, 81%); - --color-text-2: hsl(240, 43%, 99%); - - --color-accent: hsl(var(--theme-classic-hue-purple), 100%, 70%); - --color-positive: var(--theme-classic-color-green); - --color-negative: var(--theme-classic-color-red); - --color-success: var(--theme-classic-color-green); - --color-warning: var(--theme-classic-color-yellow); - --color-error: var(--theme-classic-color-red); - --color-favorite: var(--theme-classic-color-yellow); - - --color-white: #fff; - --color-black: #000; - - --color-gradient-positive: hsla(158, 49%, 48%, 0.16); - --color-gradient-negative: hsla(0, 100%, 66%, 0.16); - --color-accent-faded: hsla(var(--theme-classic-hue-purple), 100%, 70%, 0.16); - - --color-risk-low: var(--theme-classic-color-green); - --color-risk-medium: var(--theme-classic-color-yellow); - --color-risk-high: var(--theme-classic-color-red); -} - -/* Theme: Light */ -:root.theme-light { - /* Constants */ - - --theme-light-hue-purple: 234; - --theme-light-color-green: hsl(159, 67%, 39%); - --theme-light-color-yellow: hsl(36, 100%, 64%); - --theme-light-color-red: hsl(360, 73%, 61%); - - /* Parameters */ - - --layer-base-hue: var(--theme-light-hue-purple); - --layer-base-saturation: 0%; - --layer-base-lightness: 86%; - - --color-text-0: hsl(0, 0%, 55%); - --color-text-1: hsl(0, 0%, 40%); - --color-text-2: hsl(0, 0%, 5%); - - --color-accent: hsl(var(--theme-light-hue-purple), 100%, 70%); - --color-positive: var(--theme-light-color-green); - --color-negative: var(--theme-light-color-red); - --color-success: var(--theme-light-color-green); - --color-warning: var(--theme-light-color-yellow); - --color-error: var(--theme-light-color-red); - --color-favorite: var(--theme-light-color-yellow); -} - -/* Theme: Dark */ -:root.theme-dark { - /* Constants */ - - --theme-dark-hue-purple: 240; - --theme-dark-color-green: hsl(159, 67%, 39%); - --theme-dark-color-yellow: hsl(36, 100%, 64%); - --theme-dark-color-red: hsl(360, 73%, 61%); - - /* Parameters */ - - --layer-base-hue: 0; - --layer-base-saturation: 0%; - --layer-base-lightness: 16%; - - --color-text-0: hsl(0, 0%, 47%); - --color-text-1: hsl(0, 0%, 67%); - --color-text-2: hsl(0, 0%, 100%); - - --color-accent: hsl(var(--theme-dark-hue-purple), 100%, 70%); - --color-positive: var(--theme-dark-color-green); - --color-negative: var(--theme-dark-color-red); - --color-success: var(--theme-dark-color-green); - --color-warning: var(--theme-dark-color-yellow); - --color-error: var(--theme-dark-color-red); - --color-favorite: var(--theme-dark-color-yellow); -} diff --git a/src/styles/colors.ts b/src/styles/colors.ts deleted file mode 100644 index 8d26d67..0000000 --- a/src/styles/colors.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { css } from 'styled-components'; - -import { type RiskLevels } from '@/constants/abacus'; - -import { AppTheme } from '@/state/configs'; - -const BaseElements = { - [AppTheme.Classic]: { - hue: 240, - saturation: 20, - lightness: 16, - }, - [AppTheme.Dark]: { - hue: 0, - saturation: 0, - lightness: 16, - }, - [AppTheme.Light]: { - hue: 234, - saturation: 0, - lightness: 86, - }, -}; - -const LayerColors = ({ theme }: { theme: AppTheme }) => { - const { hue, saturation, lightness } = BaseElements[theme]; - - return { - layer0: `hsl(${hue}, ${saturation}%, ${lightness - 12}%)`, - layer1: `hsl(${hue}, ${saturation}%, ${lightness - 8}%)`, - layer2: `hsl(${hue}, ${saturation}%, ${lightness - 4}%)`, - layer3: `hsl(${hue}, ${saturation}%, ${lightness}%)`, - layer4: `hsl(${hue}, ${saturation}%, ${lightness + 4}%)`, - layer5: `hsl(${hue}, ${saturation - 2}%, ${lightness + 7}%)`, - layer6: `hsl(${hue}, ${saturation - 4}%, ${lightness + 10}%)`, - }; -}; - -const AccentColor = ({ theme }: { theme: AppTheme }) => { - const purpleHue = { - Classic: 240, - Dark: 240, - Light: 234, - }[theme]; - - return { - accent: `hsl(${purpleHue}, 100%, 70%)`, - }; -}; - -export const Colors = { - [AppTheme.Classic]: { - ...LayerColors({ theme: AppTheme.Classic }), - text0: 'hsl(245, 11%, 55%)', - text1: 'hsl(244, 18%, 81%)', - text2: 'hsl(240, 43%, 99%)', - - ...AccentColor({ theme: AppTheme.Classic }), - green: 'hsl(159, 67%, 39%)', - yellow: 'hsl(36, 100%, 64%)', - red: 'hsl(360, 73%, 61%)', - - positive: 'hsl(159, 67%, 39%)', - negative: 'hsl(360, 73%, 61%)', - - success: 'hsl(159, 67%, 39%)', - warning: 'hsl(36, 100%, 64%)', - error: 'hsl(360, 73%, 61%)', - - favorite: 'hsl(36, 100%, 64%)', - }, - [AppTheme.Dark]: { - ...LayerColors({ theme: AppTheme.Dark }), - text0: 'hsl(0, 0%, 47%)', - text1: 'hsl(0, 0%, 67%)', - text2: 'hsl(0, 0%, 100%)', - - ...AccentColor({ theme: AppTheme.Dark }), - green: 'hsl(159, 67%, 39%)', - yellow: 'hsl(36, 100%, 64%)', - red: 'hsl(360, 73%, 61%)', - - positive: 'hsl(159, 67%, 39%)', - negative: 'hsl(360, 73%, 61%)', - - success: 'hsl(159, 67%, 39%)', - warning: 'hsl(36, 100%, 64%)', - error: 'hsl(360, 73%, 61%)', - - favorite: 'hsl(36, 100%, 64%)', - }, - [AppTheme.Light]: { - ...LayerColors({ theme: AppTheme.Light }), - text0: 'hsl(0, 0%, 55%)', - text1: 'hsl(0, 0%, 40%)', - text2: 'hsl(0, 0%, 5%)', - - ...AccentColor({ theme: AppTheme.Light }), - green: 'hsl(159, 67%, 39%)', - yellow: 'hsl(36, 100%, 64%)', - red: 'hsl(360, 73%, 61%)', - - positive: 'hsl(159, 67%, 39%)', - negative: 'hsl(360, 73%, 61%)', - - success: 'hsl(159, 67%, 39%)', - warning: 'hsl(36, 100%, 64%)', - error: 'hsl(360, 73%, 61%)', - - favorite: 'hsl(36, 100%, 64%)', - }, -}; - -export const UsageColorFromRiskLevel = (riskLevel: RiskLevels) => - ({ - low: css` - color: var(--color-risk-low); - `, - medium: css` - color: var(--color-risk-medium); - `, - high: css` - color: var(--color-risk-high); - `, - }[riskLevel.name]); diff --git a/src/styles/formMixins.ts b/src/styles/formMixins.ts index 3345b14..d709bef 100644 --- a/src/styles/formMixins.ts +++ b/src/styles/formMixins.ts @@ -21,7 +21,7 @@ export const formMixins: Record< --input-radius: 0.5em; --input-height: var(--form-input-height); --input-width: 100%; - --input-backgroundColor: var(--color-layer-4); + --input-backgroundColor: ${({ theme }) => theme.inputBackground}; --input-borderColor: var(--color-layer-6); ${layoutMixins.row} diff --git a/src/styles/globalStyle.ts b/src/styles/globalStyle.ts new file mode 100644 index 0000000..bf534cd --- /dev/null +++ b/src/styles/globalStyle.ts @@ -0,0 +1,42 @@ +import { createGlobalStyle } from 'styled-components'; + +export const GlobalStyle = createGlobalStyle` + :root { + --color-layer-0: ${({ theme }) => theme.layer0}; + --color-layer-1: ${({ theme }) => theme.layer1}; + --color-layer-2: ${({ theme }) => theme.layer2}; + --color-layer-3: ${({ theme }) => theme.layer3}; + --color-layer-4: ${({ theme }) => theme.layer4}; + --color-layer-5: ${({ theme }) => theme.layer5}; + --color-layer-6: ${({ theme }) => theme.layer6}; + --color-layer-7: ${({ theme }) => theme.layer7}; + + --color-border: ${({ theme }) => theme.borderDefault}; + --color-border-white: ${({ theme }) => theme.borderButton}; + --color-border-red: ${({ theme }) => theme.borderDestructive}; + + --color-text-0: ${({ theme }) => theme.textTertiary}; + --color-text-1: ${({ theme }) => theme.textSecondary}; + --color-text-2: ${({ theme }) => theme.textPrimary}; + + --color-gradient-base-0: ${({ theme }) => theme.gradientBase0}; + --color-gradient-base-1: ${({ theme }) => theme.gradientBase1}; + + --color-accent: ${({ theme }) => theme.accent}; + --color-accent-faded: ${({ theme }) => theme.accentFaded}; + --color-favorite: ${({ theme }) => theme.favorite}; + + --color-success: ${({ theme }) => theme.success}; + --color-warning: ${({ theme }) => theme.warning}; + --color-error: ${({ theme }) => theme.error}; + + --color-positive: ${({ theme }) => theme.positive}; + --color-negative: ${({ theme }) => theme.negative}; + --color-gradient-positive: ${({ theme }) => theme.positiveFaded}; + --color-gradient-negative: ${({ theme }) => theme.negativeFaded}; + + --color-risk-low: ${({ theme }) => theme.riskLow}; + --color-risk-medium: ${({ theme }) => theme.riskMedium}; + --color-risk-high: ${({ theme }) => theme.riskHigh}; + } +`; diff --git a/src/styles/themes.ts b/src/styles/themes.ts new file mode 100644 index 0000000..ec96986 --- /dev/null +++ b/src/styles/themes.ts @@ -0,0 +1,157 @@ +import { AppTheme } from '@/state/configs'; +import { ThemeColors } from '@/constants/styles/colors'; +import { ColorToken, OpacityToken } from '@/constants/styles/base'; +import { generateFadedColorVariant } from '@/lib/styles'; + +const ClassicTheme: ThemeColors = { + layer0: ColorToken.GrayBlue7, + layer1: ColorToken.GrayBlue6, + layer2: ColorToken.GrayBlue5, + layer3: ColorToken.GrayBlue4, + layer4: ColorToken.GrayBlue3, + layer5: ColorToken.GrayBlue2, + layer6: ColorToken.GrayBlue1, + layer7: ColorToken.GrayBlue0, + + borderDefault: ColorToken.GrayBlue2, + borderDestructive: generateFadedColorVariant(ColorToken.Red2, OpacityToken.Opacity20), + borderButton: generateFadedColorVariant(ColorToken.White, OpacityToken.Opacity20), + + textPrimary: ColorToken.LightGray2, + textSecondary: ColorToken.GrayPurple1, + textTertiary: ColorToken.GrayPurple2, + + gradientBase0: ColorToken.DarkGray9, + gradientBase1: ColorToken.GrayBlue2, + + accent: ColorToken.Purple1, + accentFaded: generateFadedColorVariant(ColorToken.Purple1, OpacityToken.Opacity16), + favorite: ColorToken.Yellow0, + + success: ColorToken.Green1, + warning: ColorToken.Yellow0, + error: ColorToken.Red2, + + positive: ColorToken.Green1, + negative: ColorToken.Red2, + positiveFaded: generateFadedColorVariant(ColorToken.Green1, OpacityToken.Opacity16), + negativeFaded: generateFadedColorVariant(ColorToken.Red2, OpacityToken.Opacity16), + + riskLow: ColorToken.Green1, + riskMedium: ColorToken.Yellow0, + riskHigh: ColorToken.Red2, + + logoFill: ColorToken.White, + profileYellow: ColorToken.Yellow1, + profileRed: ColorToken.Red2, + + inputBackground: ColorToken.GrayBlue3, + popoverBackground: generateFadedColorVariant(ColorToken.GrayBlue4, OpacityToken.Opacity90), + switchThumbActiveBackground: ColorToken.White, + toggleBackground: ColorToken.GrayBlue3, + tooltipBackground: generateFadedColorVariant(ColorToken.GrayBlue3, OpacityToken.Opacity66), +}; + +const DarkTheme: ThemeColors = { + layer0: ColorToken.Black, + layer1: ColorToken.DarkGray11, + layer2: ColorToken.DarkGray13, + layer3: ColorToken.DarkGray10, + layer4: ColorToken.DarkGray6, + layer5: ColorToken.DarkGray5, + layer6: ColorToken.DarkGray4, + layer7: ColorToken.DarkGray2, + + borderDefault: ColorToken.DarkGray4, + borderDestructive: generateFadedColorVariant(ColorToken.Red0, OpacityToken.Opacity20), + borderButton: generateFadedColorVariant(ColorToken.White, OpacityToken.Opacity20), + + textPrimary: ColorToken.LightGray0, + textSecondary: ColorToken.MediumGray0, + textTertiary: ColorToken.DarkGray0, + + gradientBase0: ColorToken.DarkGray8, + gradientBase1: ColorToken.DarkGray5, + + accent: ColorToken.Purple0, + accentFaded: generateFadedColorVariant(ColorToken.Purple0, OpacityToken.Opacity16), + favorite: ColorToken.Yellow0, + + success: ColorToken.Green0, + warning: ColorToken.Yellow0, + error: ColorToken.Red0, + + positive: ColorToken.Green0, + negative: ColorToken.Red0, + positiveFaded: generateFadedColorVariant(ColorToken.Green0, OpacityToken.Opacity16), + negativeFaded: generateFadedColorVariant(ColorToken.Red0, OpacityToken.Opacity16), + + riskLow: ColorToken.Green0, + riskMedium: ColorToken.Yellow0, + riskHigh: ColorToken.Red0, + + logoFill: ColorToken.White, + profileYellow: ColorToken.Yellow1, + profileRed: ColorToken.Red2, + + inputBackground: ColorToken.DarkGray6, + popoverBackground: generateFadedColorVariant(ColorToken.DarkGray8, OpacityToken.Opacity90), + switchThumbActiveBackground: ColorToken.White, + toggleBackground: ColorToken.DarkGray6, + tooltipBackground: generateFadedColorVariant(ColorToken.DarkGray6, OpacityToken.Opacity66), +}; + +const LightTheme: ThemeColors = { + layer0: ColorToken.White, + layer1: ColorToken.LightGray6, + layer2: ColorToken.White, + layer3: ColorToken.LightGray1, + layer4: ColorToken.White, + layer5: ColorToken.LightGray4, + layer6: ColorToken.LightGray9, + layer7: ColorToken.MediumGray1, + + borderDefault: ColorToken.LightGray10, + borderDestructive: generateFadedColorVariant(ColorToken.Red1, OpacityToken.Opacity20), + borderButton: generateFadedColorVariant(ColorToken.Black, OpacityToken.Opacity20), + + textPrimary: ColorToken.DarkGray12, + textSecondary: ColorToken.DarkGray3, + textTertiary: ColorToken.DarkGray1, + + gradientBase0: ColorToken.LightGray8, + gradientBase1: ColorToken.LightGray5, + + accent: ColorToken.Purple0, + accentFaded: generateFadedColorVariant(ColorToken.Purple0, OpacityToken.Opacity16), + favorite: ColorToken.Yellow0, + + success: ColorToken.Green2, + warning: ColorToken.Yellow0, + error: ColorToken.Red1, + + positive: ColorToken.Green2, + negative: ColorToken.Red1, + positiveFaded: generateFadedColorVariant(ColorToken.Green2, OpacityToken.Opacity16), + negativeFaded: generateFadedColorVariant(ColorToken.Red1, OpacityToken.Opacity16), + + riskLow: ColorToken.Green2, + riskMedium: ColorToken.Yellow0, + riskHigh: ColorToken.Red1, + + logoFill: ColorToken.Black, + profileYellow: ColorToken.Yellow1, + profileRed: ColorToken.Red2, + + inputBackground: ColorToken.White, + popoverBackground: generateFadedColorVariant(ColorToken.LightGray8, OpacityToken.Opacity90), + switchThumbActiveBackground: ColorToken.White, + toggleBackground: ColorToken.LightGray4, + tooltipBackground: generateFadedColorVariant(ColorToken.LightGray7, OpacityToken.Opacity66), +}; + +export const Themes = { + [AppTheme.Classic]: ClassicTheme, + [AppTheme.Dark]: DarkTheme, + [AppTheme.Light]: LightTheme, +}; diff --git a/src/views/forms/TradeForm/TradeSideToggle.tsx b/src/views/forms/TradeForm/TradeSideToggle.tsx index ab269e3..8af0c6a 100644 --- a/src/views/forms/TradeForm/TradeSideToggle.tsx +++ b/src/views/forms/TradeForm/TradeSideToggle.tsx @@ -44,6 +44,7 @@ export const TradeSideToggle = memo(() => { const ToggleContainer = styled(ToggleGroup)<{ value: OrderSide }>` --toggle-radius: 0.5em; --toggle-color: var(--color-negative); + --toggle-background: ${({ theme }) => theme.toggleBackground}; ${({ value }) => value === OrderSide.BUY && @@ -52,7 +53,7 @@ const ToggleContainer = styled(ToggleGroup)<{ value: OrderSide }>` `} border-radius: var(--toggle-radius); - background-color: var(--color-layer-4); + background-color: var(--toggle-background); position: relative; > button {