/ src / utils / theme.tsx
theme.tsx
  1  import {
  2    CheckCircleIcon,
  3    ChevronDownIcon,
  4    ExclamationCircleIcon,
  5    ExclamationIcon,
  6    InformationCircleIcon,
  7  } from '@heroicons/react/outline';
  8  import { SvgIcon, Theme, ThemeOptions } from '@mui/material';
  9  import { createTheme } from '@mui/material/styles';
 10  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
 11  // @ts-ignore
 12  import { ColorPartial } from '@mui/material/styles/createPalette';
 13  import React from 'react';
 14  
 15  const theme = createTheme();
 16  const {
 17    typography: { pxToRem },
 18  } = theme;
 19  
 20  const FONT = 'Inter, Arial';
 21  
 22  declare module '@mui/material/styles/createPalette' {
 23    interface PaletteColor extends ColorPartial {}
 24  
 25    interface TypeText {
 26      muted: string;
 27    }
 28  
 29    interface TypeBackground {
 30      default: string;
 31      paper: string;
 32      surface: string;
 33      surface2: string;
 34      header: string;
 35      disabled: string;
 36    }
 37  
 38    interface Palette {
 39      gradients: {
 40        aaveGradient: string;
 41        newGradient: string;
 42      };
 43      other: {
 44        standardInputLine: string;
 45      };
 46    }
 47  
 48    interface PaletteOptions {
 49      gradients: {
 50        aaveGradient: string;
 51        newGradient: string;
 52      };
 53    }
 54  }
 55  
 56  interface TypographyCustomVariants {
 57    display1: React.CSSProperties;
 58    subheader1: React.CSSProperties;
 59    subheader2: React.CSSProperties;
 60    description: React.CSSProperties;
 61    buttonL: React.CSSProperties;
 62    buttonM: React.CSSProperties;
 63    buttonS: React.CSSProperties;
 64    helperText: React.CSSProperties;
 65    tooltip: React.CSSProperties;
 66    main21: React.CSSProperties;
 67    secondary21: React.CSSProperties;
 68    main16: React.CSSProperties;
 69    secondary16: React.CSSProperties;
 70    main14: React.CSSProperties;
 71    secondary14: React.CSSProperties;
 72    main12: React.CSSProperties;
 73    secondary12: React.CSSProperties;
 74  }
 75  
 76  declare module '@mui/material/styles' {
 77    interface TypographyVariants extends TypographyCustomVariants {}
 78  
 79    // allow configuration using `createTheme`
 80    interface TypographyVariantsOptions extends TypographyCustomVariants {}
 81  
 82    interface BreakpointOverrides {
 83      xsm: true;
 84      xxl: true;
 85      mdlg: true;
 86    }
 87  }
 88  
 89  // Update the Typography's variant prop options
 90  declare module '@mui/material/Typography' {
 91    interface TypographyPropsVariantOverrides {
 92      display1: true;
 93      subheader1: true;
 94      subheader2: true;
 95      description: true;
 96      buttonL: true;
 97      buttonM: true;
 98      buttonS: true;
 99      helperText: true;
100      tooltip: true;
101      main21: true;
102      secondary21: true;
103      main16: true;
104      secondary16: true;
105      main14: true;
106      secondary14: true;
107      main12: true;
108      secondary12: true;
109      h5: false;
110      h6: false;
111      subtitle1: false;
112      subtitle2: false;
113      body1: false;
114      body2: false;
115      button: false;
116      overline: false;
117    }
118  }
119  
120  declare module '@mui/material/Button' {
121    interface ButtonPropsVariantOverrides {
122      surface: true;
123      gradient: true;
124    }
125  }
126  
127  export const getDesignTokens = (mode: 'light' | 'dark') => {
128    const getColor = (lightColor: string, darkColor: string) =>
129      mode === 'dark' ? darkColor : lightColor;
130  
131    return {
132      breakpoints: {
133        keys: ['xs', 'xsm', 'sm', 'md', 'lg', 'xl', 'xxl'],
134        values: { xs: 0, xsm: 640, sm: 760, md: 960, mdlg: 1125, lg: 1280, xl: 1575, xxl: 1800 },
135      },
136      palette: {
137        mode,
138        primary: {
139          main: getColor('#383D51', '#EAEBEF'),
140          light: getColor('#62677B', '#F1F1F3'),
141          dark: getColor('#292E41', '#D2D4DC'),
142          contrast: getColor('#FFFFFF', '#0F121D'),
143        },
144        secondary: {
145          main: getColor('#FF607B', '#F48FB1'),
146          light: getColor('#FF607B', '#F6A5C0'),
147          dark: getColor('#B34356', '#AA647B'),
148        },
149        error: {
150          main: getColor('#BC0000B8', '#F44336'),
151          light: getColor('#D26666', '#E57373'),
152          dark: getColor('#BC0000', '#D32F2F'),
153          '100': getColor('#4F1919', '#FBB4AF'), // for alert text
154          '200': getColor('#F9EBEB', '#2E0C0A'), // for alert background
155        },
156        warning: {
157          main: getColor('#F89F1A', '#FFA726'),
158          light: getColor('#FFCE00', '#FFB74D'),
159          dark: getColor('#C67F15', '#F57C00'),
160          '100': getColor('#63400A', '#FFDCA8'), // for alert text
161          '200': getColor('#FEF5E8', '#301E04'), // for alert background
162        },
163        info: {
164          main: getColor('#0062D2', '#29B6F6'),
165          light: getColor('#0062D2', '#4FC3F7'),
166          dark: getColor('#002754', '#0288D1'),
167          '100': getColor('#002754', '#A9E2FB'), // for alert text
168          '200': getColor('#E5EFFB', '#071F2E'), // for alert background
169        },
170        success: {
171          main: getColor('#4CAF50', '#66BB6A'),
172          light: getColor('#90FF95', '#90FF95'),
173          dark: getColor('#318435', '#388E3C'),
174          '100': getColor('#1C4B1E', '#C2E4C3'), // for alert text
175          '200': getColor('#ECF8ED', '#0A130B'), // for alert background
176        },
177        text: {
178          primary: getColor('#303549', '#F1F1F3'),
179          secondary: getColor('#62677B', '#A5A8B6'),
180          disabled: getColor('#D2D4DC', '#62677B'),
181          muted: getColor('#A5A8B6', '#8E92A3'),
182          highlight: getColor('#383D51', '#C9B3F9'),
183        },
184        background: {
185          default: getColor('#F1F1F3', '#1B2030'),
186          paper: getColor('#FFFFFF', '#292E41'),
187          surface: getColor('#F7F7F9', '#383D51'),
188          surface2: getColor('#F9F9FB', '#383D51'),
189          header: getColor('#2B2D3C', '#1B2030'),
190          disabled: getColor('#EAEBEF', '#EBEBEF14'),
191        },
192        divider: getColor('#EAEBEF', '#EBEBEF14'),
193        action: {
194          active: getColor('#8E92A3', '#EBEBEF8F'),
195          hover: getColor('#F1F1F3', '#EBEBEF14'),
196          selected: getColor('#EAEBEF', '#EBEBEF29'),
197          disabled: getColor('#BBBECA', '#EBEBEF4D'),
198          disabledBackground: getColor('#EAEBEF', '#EBEBEF1F'),
199          focus: getColor('#F1F1F3', '#EBEBEF1F'),
200        },
201        other: {
202          standardInputLine: getColor('#383D511F', '#EBEBEF6B'),
203        },
204        gradients: {
205          aaveGradient: 'linear-gradient(248.86deg, #B6509E 10.51%, #2EBAC6 93.41%)',
206          newGradient: 'linear-gradient(79.67deg, #8C3EBC 0%, #007782 95.82%)',
207        },
208      },
209      spacing: 4,
210      typography: {
211        fontFamily: FONT,
212        h5: undefined,
213        h6: undefined,
214        subtitle1: undefined,
215        subtitle2: undefined,
216        body1: undefined,
217        body2: undefined,
218        button: undefined,
219        overline: undefined,
220        display1: {
221          fontFamily: FONT,
222          fontWeight: 700,
223          letterSpacing: pxToRem(0.25),
224          lineHeight: '123.5%',
225          fontSize: pxToRem(32),
226        },
227        h1: {
228          fontFamily: FONT,
229          fontWeight: 700,
230          letterSpacing: pxToRem(0.25),
231          lineHeight: '123.5%',
232          fontSize: pxToRem(28),
233        },
234        h2: {
235          fontFamily: FONT,
236          fontWeight: 600,
237          letterSpacing: 'unset',
238          lineHeight: '133.4%',
239          fontSize: pxToRem(21),
240        },
241        h3: {
242          fontFamily: FONT,
243          fontWeight: 600,
244          letterSpacing: pxToRem(0.15),
245          lineHeight: '160%',
246          fontSize: pxToRem(18),
247        },
248        h4: {
249          fontFamily: FONT,
250          fontWeight: 600,
251          letterSpacing: pxToRem(0.15),
252          lineHeight: pxToRem(24),
253          fontSize: pxToRem(16),
254        },
255        subheader1: {
256          fontFamily: FONT,
257          fontWeight: 600,
258          letterSpacing: pxToRem(0.15),
259          lineHeight: pxToRem(20),
260          fontSize: pxToRem(14),
261        },
262        subheader2: {
263          fontFamily: FONT,
264          fontWeight: 500,
265          letterSpacing: pxToRem(0.1),
266          lineHeight: pxToRem(16),
267          fontSize: pxToRem(12),
268        },
269        description: {
270          fontFamily: FONT,
271          fontWeight: 400,
272          letterSpacing: pxToRem(0.15),
273          lineHeight: '143%',
274          fontSize: pxToRem(14),
275        },
276        caption: {
277          fontFamily: FONT,
278          fontWeight: 400,
279          letterSpacing: pxToRem(0.15),
280          lineHeight: pxToRem(16),
281          fontSize: pxToRem(12),
282        },
283        buttonL: {
284          fontFamily: FONT,
285          fontWeight: 500,
286          letterSpacing: pxToRem(0.46),
287          lineHeight: pxToRem(24),
288          fontSize: pxToRem(16),
289        },
290        buttonM: {
291          fontFamily: FONT,
292          fontWeight: 500,
293          lineHeight: pxToRem(24),
294          fontSize: pxToRem(14),
295        },
296        buttonS: {
297          fontFamily: FONT,
298          fontWeight: 600,
299          letterSpacing: pxToRem(0.46),
300          lineHeight: pxToRem(20),
301          textTransform: 'uppercase',
302          fontSize: pxToRem(10),
303        },
304        helperText: {
305          fontFamily: FONT,
306          fontWeight: 400,
307          letterSpacing: pxToRem(0.4),
308          lineHeight: pxToRem(12),
309          fontSize: pxToRem(10),
310        },
311        tooltip: {
312          fontFamily: FONT,
313          fontWeight: 400,
314          letterSpacing: pxToRem(0.15),
315          lineHeight: pxToRem(16),
316          fontSize: pxToRem(12),
317        },
318        main21: {
319          fontFamily: FONT,
320          fontWeight: 800,
321          lineHeight: '133.4%',
322          fontSize: pxToRem(21),
323        },
324        secondary21: {
325          fontFamily: FONT,
326          fontWeight: 500,
327          lineHeight: '133.4%',
328          fontSize: pxToRem(21),
329        },
330        main16: {
331          fontFamily: FONT,
332          fontWeight: 600,
333          letterSpacing: pxToRem(0.15),
334          lineHeight: pxToRem(24),
335          fontSize: pxToRem(16),
336        },
337        secondary16: {
338          fontFamily: FONT,
339          fontWeight: 500,
340          letterSpacing: pxToRem(0.15),
341          lineHeight: pxToRem(24),
342          fontSize: pxToRem(16),
343        },
344        main14: {
345          fontFamily: FONT,
346          fontWeight: 600,
347          letterSpacing: pxToRem(0.15),
348          lineHeight: pxToRem(20),
349          fontSize: pxToRem(14),
350        },
351        secondary14: {
352          fontFamily: FONT,
353          fontWeight: 500,
354          letterSpacing: pxToRem(0.15),
355          lineHeight: pxToRem(20),
356          fontSize: pxToRem(14),
357        },
358        main12: {
359          fontFamily: FONT,
360          fontWeight: 600,
361          letterSpacing: pxToRem(0.1),
362          lineHeight: pxToRem(16),
363          fontSize: pxToRem(12),
364        },
365        secondary12: {
366          fontFamily: FONT,
367          fontWeight: 500,
368          letterSpacing: pxToRem(0.1),
369          lineHeight: pxToRem(16),
370          fontSize: pxToRem(12),
371        },
372      },
373    } as ThemeOptions;
374  };
375  
376  export function getThemedComponents(theme: Theme) {
377    return {
378      components: {
379        MuiSkeleton: {
380          styleOverrides: {
381            root: {
382              transform: 'unset',
383            },
384          },
385        },
386        MuiOutlinedInput: {
387          styleOverrides: {
388            root: {
389              borderRadius: '6px',
390              borderColor: theme.palette.divider,
391              '&:hover .MuiOutlinedInput-notchedOutline': {
392                borderColor: '#CBCDD8',
393              },
394              '&.Mui-focused .MuiOutlinedInput-notchedOutline': {
395                borderColor: '#CBCDD8',
396              },
397            },
398          },
399        },
400        MuiSlider: {
401          styleOverrides: {
402            root: {
403              '& .MuiSlider-thumb': {
404                color: theme.palette.mode === 'light' ? '#62677B' : '#C9B3F9',
405              },
406              '& .MuiSlider-track': {
407                color: theme.palette.mode === 'light' ? '#383D51' : '#9C93B3',
408              },
409            },
410          },
411        },
412        MuiButton: {
413          defaultProps: {
414            disableElevation: true,
415          },
416          styleOverrides: {
417            root: {
418              borderRadius: '4px',
419            },
420            sizeLarge: {
421              ...theme.typography.buttonL,
422              padding: '10px 24px',
423            },
424            sizeMedium: {
425              ...theme.typography.buttonM,
426              padding: '6px 12px',
427            },
428            sizeSmall: {
429              ...theme.typography.buttonS,
430              padding: '0 6px',
431            },
432          },
433          variants: [
434            {
435              props: { variant: 'surface' },
436              style: {
437                color: theme.palette.common.white,
438                border: '1px solid',
439                borderColor: '#EBEBED1F',
440                backgroundColor: '#383D51',
441                '&:hover, &.Mui-focusVisible': {
442                  backgroundColor: theme.palette.background.header,
443                },
444              },
445            },
446            {
447              props: { variant: 'gradient' },
448              style: {
449                color: theme.palette.common.white,
450                background: theme.palette.gradients.aaveGradient,
451                transition: 'all 0.2s ease',
452                '&:hover, &.Mui-focusVisible': {
453                  background: theme.palette.gradients.aaveGradient,
454                  opacity: '0.9',
455                },
456              },
457            },
458            {
459              props: { color: 'primary', variant: 'outlined' },
460              style: {
461                background: theme.palette.background.surface,
462                borderColor: theme.palette.divider,
463              },
464            },
465          ],
466        },
467        MuiTypography: {
468          defaultProps: {
469            variant: 'description',
470            variantMapping: {
471              display1: 'h1',
472              h1: 'h1',
473              h2: 'h2',
474              h3: 'h3',
475              h4: 'h4',
476              subheader1: 'p',
477              subheader2: 'p',
478              caption: 'p',
479              description: 'p',
480              buttonL: 'p',
481              buttonM: 'p',
482              buttonS: 'p',
483              main12: 'p',
484              main14: 'p',
485              main16: 'p',
486              main21: 'p',
487              secondary12: 'p',
488              secondary14: 'p',
489              secondary16: 'p',
490              secondary21: 'p',
491              helperText: 'span',
492              tooltip: 'span',
493            },
494          },
495        },
496        MuiLink: {
497          defaultProps: {
498            variant: 'description',
499          },
500        },
501        MuiMenu: {
502          defaultProps: {
503            PaperProps: {
504              elevation: 0,
505              variant: 'outlined',
506              style: {
507                minWidth: 240,
508                marginTop: '4px',
509              },
510            },
511          },
512        },
513        MuiList: {
514          styleOverrides: {
515            root: {
516              '.MuiMenuItem-root+.MuiDivider-root, .MuiDivider-root': {
517                marginTop: '4px',
518                marginBottom: '4px',
519              },
520            },
521            padding: {
522              paddingTop: '4px',
523              paddingBottom: '4px',
524            },
525          },
526        },
527        MuiMenuItem: {
528          styleOverrides: {
529            root: {
530              padding: '12px 16px',
531            },
532          },
533        },
534        MuiListItemText: {
535          styleOverrides: {
536            root: {
537              ...theme.typography.subheader1,
538            },
539          },
540        },
541        MuiListItemIcon: {
542          styleOverrides: {
543            root: {
544              color: theme.palette.primary.light,
545              minWidth: 'unset !important',
546              marginRight: '12px',
547            },
548          },
549        },
550        MuiDivider: {
551          styleOverrides: {
552            root: {
553              marginTop: 0,
554              marginBottom: 0,
555            },
556          },
557        },
558        MuiPaper: {
559          styleOverrides: {
560            root: {
561              borderRadius: '4px',
562            },
563          },
564          variants: [
565            {
566              props: { variant: 'outlined' },
567              style: {
568                border: `1px solid ${theme.palette.divider}`,
569                boxShadow: '0px 0px 2px rgba(0, 0, 0, 0.2), 0px 2px 10px rgba(0, 0, 0, 0.1)',
570                background:
571                  theme.palette.mode === 'light'
572                    ? theme.palette.background.paper
573                    : theme.palette.background.surface,
574              },
575            },
576            {
577              props: { variant: 'elevation' },
578              style: {
579                boxShadow: '0px 2px 1px rgba(0, 0, 0, 0.05), 0px 0px 1px rgba(0, 0, 0, 0.25)',
580                ...(theme.palette.mode === 'dark' ? { backgroundImage: 'none' } : {}),
581              },
582            },
583          ],
584        },
585        MuiContainer: {
586          styleOverrides: {
587            root: {
588              display: 'flex',
589              flexDirection: 'column',
590              flex: 1,
591              paddingBottom: '39px',
592              [theme.breakpoints.up('xs')]: {
593                paddingLeft: '8px',
594                paddingRight: '8px',
595              },
596              [theme.breakpoints.up('xsm')]: {
597                paddingLeft: '20px',
598                paddingRight: '20px',
599              },
600              [theme.breakpoints.up('sm')]: {
601                paddingLeft: '48px',
602                paddingRight: '48px',
603              },
604              [theme.breakpoints.up('md')]: {
605                paddingLeft: '96px',
606                paddingRight: '96px',
607              },
608              [theme.breakpoints.up('lg')]: {
609                paddingLeft: '20px',
610                paddingRight: '20px',
611              },
612              [theme.breakpoints.up('xl')]: {
613                maxWidth: 'unset',
614                paddingLeft: '96px',
615                paddingRight: '96px',
616              },
617              [theme.breakpoints.up('xxl')]: {
618                paddingLeft: 0,
619                paddingRight: 0,
620                maxWidth: '1440px',
621              },
622            },
623          },
624        },
625        MuiSwitch: {
626          styleOverrides: {
627            root: {
628              height: 20 + 6 * 2,
629              width: 34 + 6 * 2,
630              padding: 6,
631            },
632            switchBase: {
633              padding: 8,
634              '&.Mui-checked': {
635                transform: 'translateX(14px)',
636                '& + .MuiSwitch-track': {
637                  backgroundColor: theme.palette.success.main,
638                  opacity: 1,
639                },
640              },
641              '&.Mui-disabled': {
642                opacity: theme.palette.mode === 'dark' ? 0.3 : 0.7,
643              },
644            },
645            thumb: {
646              color: theme.palette.common.white,
647              borderRadius: '6px',
648              width: '16px',
649              height: '16px',
650              boxShadow: '0px 1px 1px rgba(0, 0, 0, 0.12)',
651            },
652            track: {
653              opacity: 1,
654              backgroundColor: theme.palette.action.active,
655              borderRadius: '8px',
656            },
657          },
658        },
659        MuiIcon: {
660          variants: [
661            {
662              props: { fontSize: 'large' },
663              style: {
664                fontSize: pxToRem(32),
665              },
666            },
667          ],
668        },
669        MuiTableCell: {
670          styleOverrides: {
671            root: {
672              borderColor: theme.palette.divider,
673            },
674          },
675        },
676        MuiAlert: {
677          styleOverrides: {
678            root: {
679              boxShadow: 'none',
680              borderRadius: '4px',
681              padding: '8px 12px',
682              ...theme.typography.caption,
683              alignItems: 'flex-start',
684              '.MuiAlert-message': {
685                padding: 0,
686                paddingTop: '2px',
687                paddingBottom: '2px',
688              },
689              '.MuiAlert-icon': {
690                padding: 0,
691                opacity: 1,
692                '.MuiSvgIcon-root': {
693                  fontSize: pxToRem(20),
694                },
695              },
696              a: {
697                ...theme.typography.caption,
698                fontWeight: 500,
699                textDecoration: 'underline',
700                '&:hover': {
701                  textDecoration: 'none',
702                },
703              },
704              '.MuiButton-text': {
705                ...theme.typography.caption,
706                fontWeight: 500,
707                textDecoration: 'underline',
708                padding: 0,
709                margin: 0,
710                minWidth: 'unset',
711                '&:hover': {
712                  textDecoration: 'none',
713                  background: 'transparent',
714                },
715              },
716            },
717          },
718          defaultProps: {
719            iconMapping: {
720              error: (
721                <SvgIcon color="error">
722                  <ExclamationIcon />
723                </SvgIcon>
724              ),
725              info: (
726                <SvgIcon color="info">
727                  <InformationCircleIcon />
728                </SvgIcon>
729              ),
730              success: (
731                <SvgIcon color="success">
732                  <CheckCircleIcon />
733                </SvgIcon>
734              ),
735              warning: (
736                <SvgIcon color="warning">
737                  <ExclamationCircleIcon />
738                </SvgIcon>
739              ),
740            },
741          },
742          variants: [
743            {
744              props: { severity: 'error' },
745              style: {
746                color: theme.palette.error['100'],
747                background: theme.palette.error['200'],
748                a: {
749                  color: theme.palette.error['100'],
750                },
751                '.MuiButton-text': {
752                  color: theme.palette.error['100'],
753                },
754              },
755            },
756            {
757              props: { severity: 'info' },
758              style: {
759                color: theme.palette.info['100'],
760                background: theme.palette.info['200'],
761                a: {
762                  color: theme.palette.info['100'],
763                },
764                '.MuiButton-text': {
765                  color: theme.palette.info['100'],
766                },
767              },
768            },
769            {
770              props: { severity: 'success' },
771              style: {
772                color: theme.palette.success['100'],
773                background: theme.palette.success['200'],
774                a: {
775                  color: theme.palette.success['100'],
776                },
777                '.MuiButton-text': {
778                  color: theme.palette.success['100'],
779                },
780              },
781            },
782            {
783              props: { severity: 'warning' },
784              style: {
785                color: theme.palette.warning['100'],
786                background: theme.palette.warning['200'],
787                a: {
788                  color: theme.palette.warning['100'],
789                },
790                '.MuiButton-text': {
791                  color: theme.palette.warning['100'],
792                },
793              },
794            },
795          ],
796        },
797        MuiCssBaseline: {
798          styleOverrides: {
799            body: {
800              fontFamily: FONT,
801              fontWeight: 400,
802              fontSize: pxToRem(14),
803              minWidth: '375px',
804              '> div:first-of-type': {
805                minHeight: '100vh',
806                display: 'flex',
807                flexDirection: 'column',
808              },
809            },
810          },
811        },
812        MuiSvgIcon: {
813          styleOverrides: {
814            colorPrimary: {
815              color: theme.palette.primary.light,
816            },
817          },
818        },
819        MuiSelect: {
820          defaultProps: {
821            IconComponent: (props) => (
822              <SvgIcon sx={{ fontSize: '16px' }} {...props}>
823                <ChevronDownIcon />
824              </SvgIcon>
825            ),
826          },
827          styleOverrides: {
828            outlined: {
829              backgroundColor: theme.palette.background.surface,
830              ...theme.typography.buttonM,
831              padding: '6px 12px',
832              color: theme.palette.primary.light,
833            },
834          },
835        },
836        MuiLinearProgress: {
837          styleOverrides: {
838            bar1Indeterminate: {
839              background: theme.palette.gradients.aaveGradient,
840            },
841            bar2Indeterminate: {
842              background: theme.palette.gradients.aaveGradient,
843            },
844          },
845        },
846      },
847    } as ThemeOptions;
848  }