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
This commit is contained in:
moo-onthelawn 2024-01-24 17:03:44 -05:00 committed by GitHub
parent b86a346be3
commit 17fce5417a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 523 additions and 437 deletions

View File

@ -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
</SelectMenu>
</StoryHeader>
<hr />
<StoryContent>{children}</StoryContent>
</Provider>
<AppThemeProvider>
<GlobalStyle />
<StoryContent>{children}</StoryContent>
</AppThemeProvider>
</Provider>
);
};

View File

@ -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 (
<Styled.Content isShowingHeader={isShowingHeader} isShowingFooter={isShowingFooter}>
{isNotTablet && <HeaderDesktop />}
<>
<GlobalStyle />
<Styled.Content isShowingHeader={isShowingHeader} isShowingFooter={isShowingFooter}>
{isNotTablet && <HeaderDesktop />}
<Styled.Main>
<Suspense fallback={<LoadingSpace id="main" />}>
<Routes>
<Route path={AppRoute.Trade}>
<Route path=":market" element={<TradePage />} />
<Route path={AppRoute.Trade} element={<TradePage />} />
</Route>
<Styled.Main>
<Suspense fallback={<LoadingSpace id="main" />}>
<Routes>
<Route path={AppRoute.Trade}>
<Route path=":market" element={<TradePage />} />
<Route path={AppRoute.Trade} element={<TradePage />} />
</Route>
<Route path={AppRoute.Markets} element={<MarketsPage />} />
<Route path={`/${chainTokenLabel}`} element={<RewardsPage />} />
{isTablet && (
<>
<Route path={AppRoute.Alerts} element={<AlertsPage />} />
<Route path={AppRoute.Profile} element={<ProfilePage />} />
<Route path={`${AppRoute.Settings}/*`} element={<SettingsPage />} />
</>
)}
<Route path={AppRoute.Markets} element={<MarketsPage />} />
<Route path={`/${chainTokenLabel}`} element={<RewardsPage />} />
{isTablet && (
<>
<Route path={AppRoute.Alerts} element={<AlertsPage />} />
<Route path={AppRoute.Profile} element={<ProfilePage />} />
<Route path={`${AppRoute.Settings}/*`} element={<SettingsPage />} />
</>
)}
<Route element={<GuardedMobileRoute />}>
<Route path={`${AppRoute.Portfolio}/*`} element={<PortfolioPage />} />
</Route>
<Route element={<GuardedMobileRoute />}>
<Route path={`${AppRoute.Portfolio}/*`} element={<PortfolioPage />} />
</Route>
<Route path={AppRoute.Terms} element={<TermsOfUsePage />} />
<Route path={AppRoute.Privacy} element={<PrivacyPolicyPage />} />
<Route path={AppRoute.Terms} element={<TermsOfUsePage />} />
<Route path={AppRoute.Privacy} element={<PrivacyPolicyPage />} />
<Route path="*" element={<Navigate to={DEFAULT_TRADE_ROUTE} replace />} />
</Routes>
</Suspense>
</Styled.Main>
<Route path="*" element={<Navigate to={DEFAULT_TRADE_ROUTE} replace />} />
</Routes>
</Suspense>
</Styled.Main>
{isTablet ? <FooterMobile /> : <FooterDesktop />}
{isTablet ? <FooterMobile /> : <FooterDesktop />}
<Styled.NotificationsToastArea />
<Styled.NotificationsToastArea />
<Styled.DialogArea ref={setDialogArea}>
<DialogManager />
</Styled.DialogArea>
<Styled.DialogArea ref={setDialogArea}>
<DialogManager />
</Styled.DialogArea>
<GlobalCommandDialog />
</Styled.Content>
<GlobalCommandDialog />
</Styled.Content>
</>
);
};
@ -131,6 +136,7 @@ const providers = [
wrapProvider(LocalNotificationsProvider),
wrapProvider(NotificationsProvider),
wrapProvider(DialogAreaProvider),
wrapProvider(AppThemeProvider),
];
const App = () => {

View File

@ -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;

View File

@ -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);

View File

@ -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;
};

View File

@ -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);

View File

@ -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;

View File

@ -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',
}

View File

@ -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;
};

View File

@ -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) {

16
src/hooks/useAppTheme.tsx Normal file
View File

@ -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 <ThemeProvider theme={useAppThemeContext()} {...props} />
};
export const useAppThemeContext = () => {
const theme: AppTheme = useSelector(getAppTheme);
return Themes[theme];
}

View File

@ -1,45 +1,52 @@
const LogoShortIcon: React.FC<{ id?: string }> = ({ id }: { id?: string }) => (
<svg
width="135"
height="145"
viewBox="0 0 135 145"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M100.986 0L0 144.988H31.0048L132.514 0H100.986Z" fill="white" />
<path
d="M34.2346 0L63.9475 42.7232L48.4451 66.0268L2.58386 0H34.2346Z"
fill={`url(#${id ? `${id}_logo_gradient_0` : 'paint0_linear'})`}
/>
<path
d="M103.995 145L71.0526 97.7455L86.555 75.0893L135 145H103.995Z"
fill={`url(#${id ? `${id}_logo_gradient_1` : 'paint1_linear'})`}
/>
<defs>
<linearGradient
id={id ? `${id}_logo_gradient_0` : 'paint0_linear'}
x1="27.1293"
y1="9.0625"
x2="69.773"
y2="60.4324"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="white" />
<stop offset="1" stopColor="white" stopOpacity="0.55" />
</linearGradient>
<linearGradient
id={id ? `${id}_logo_gradient_1` : 'paint1_linear'}
x1="111.1"
y1="133.996"
x2="58.6959"
y2="63.4999"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#6966FF" />
<stop offset="1" stopColor="#6966FF" stopOpacity="0.36" />
</linearGradient>
</defs>
</svg>
);
import { useAppThemeContext } from '@/hooks/useAppTheme';
const LogoShortIcon: React.FC<{ id?: string }> = ({ id }: { id?: string }) => {
const theme = useAppThemeContext();
const fill = theme.logoFill;
return (
<svg
width="135"
height="145"
viewBox="0 0 135 145"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M100.986 0L0 144.988H31.0048L132.514 0H100.986Z" fill={fill} />
<path
d="M34.2346 0L63.9475 42.7232L48.4451 66.0268L2.58386 0H34.2346Z"
fill={`url(#${id ? `${id}_logo_gradient_0` : 'paint0_linear'})`}
/>
<path
d="M103.995 145L71.0526 97.7455L86.555 75.0893L135 145H103.995Z"
fill={`url(#${id ? `${id}_logo_gradient_1` : 'paint1_linear'})`}
/>
<defs>
<linearGradient
id={id ? `${id}_logo_gradient_0` : 'paint0_linear'}
x1="27.1293"
y1="9.0625"
x2="69.773"
y2="60.4324"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={fill} />
<stop offset="1" stopColor={fill} stopOpacity="0.55" />
</linearGradient>
<linearGradient
id={id ? `${id}_logo_gradient_1` : 'paint1_linear'}
x1="111.1"
y1="133.996"
x2="58.6959"
y2="63.4999"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#6966FF" />
<stop offset="1" stopColor="#6966FF" stopOpacity="0.36" />
</linearGradient>
</defs>
</svg>
);
};
export default LogoShortIcon;

View File

@ -1,6 +1,5 @@
@import 'styles/fonts.css';
@import 'styles/text.css';
@import 'styles/colors.css';
@import 'styles/animations.css';
:root {

20
src/lib/styles.ts Normal file
View File

@ -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}`;
};

View File

@ -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,
},
};
};

View File

@ -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%
);
`;

View File

@ -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);
}

View File

@ -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]);

View File

@ -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}

42
src/styles/globalStyle.ts Normal file
View File

@ -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};
}
`;

157
src/styles/themes.ts Normal file
View File

@ -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,
};

View File

@ -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 {