aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/frontend/src/theme/ThemeProvider.tsx
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-12-03 03:34:31 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-12-09 00:07:39 +0100
commitd02125201f39a0620aedab9f350cff84fca22bd3 (patch)
tree1b2c829323572782ebc792837a4307d72f2e46bf /subprojects/frontend/src/theme/ThemeProvider.tsx
parentrefactor(frontend): scrollbar improvements (diff)
downloadrefinery-d02125201f39a0620aedab9f350cff84fca22bd3.tar.gz
refinery-d02125201f39a0620aedab9f350cff84fca22bd3.tar.zst
refinery-d02125201f39a0620aedab9f350cff84fca22bd3.zip
refactor(frontend): theme improvements
Diffstat (limited to 'subprojects/frontend/src/theme/ThemeProvider.tsx')
-rw-r--r--subprojects/frontend/src/theme/ThemeProvider.tsx441
1 files changed, 242 insertions, 199 deletions
diff --git a/subprojects/frontend/src/theme/ThemeProvider.tsx b/subprojects/frontend/src/theme/ThemeProvider.tsx
index 85844d3c..7bda1ede 100644
--- a/subprojects/frontend/src/theme/ThemeProvider.tsx
+++ b/subprojects/frontend/src/theme/ThemeProvider.tsx
@@ -1,15 +1,13 @@
1import { 1import {
2 alpha, 2 alpha,
3 createTheme, 3 createTheme,
4 type Components,
5 type CSSObject,
6 responsiveFontSizes, 4 responsiveFontSizes,
7 type Theme, 5 type Theme,
8 type ThemeOptions, 6 type ThemeOptions,
9 ThemeProvider as MaterialUiThemeProvider, 7 ThemeProvider as MaterialUiThemeProvider,
10 type TypographyStyle, 8 type TypographyStyle,
11 type TypographyVariantsOptions,
12 useTheme, 9 useTheme,
10 type CSSObject,
13} from '@mui/material/styles'; 11} from '@mui/material/styles';
14import { observer } from 'mobx-react-lite'; 12import { observer } from 'mobx-react-lite';
15import { type ReactNode, createContext, useContext } from 'react'; 13import { type ReactNode, createContext, useContext } from 'react';
@@ -22,13 +20,11 @@ interface OuterPalette {
22} 20}
23 21
24interface HighlightPalette { 22interface HighlightPalette {
25 cursor: string;
26 number: string; 23 number: string;
27 parameter: string; 24 parameter: string;
28 comment: string; 25 comment: string;
29 activeLine: string; 26 activeLine: string;
30 selection: string; 27 selection: string;
31 lineNumber: string;
32 foldPlaceholder: string; 28 foldPlaceholder: string;
33 activeLintRange: string; 29 activeLintRange: string;
34 occurences: { 30 occurences: {
@@ -49,11 +45,10 @@ declare module '@mui/material/styles' {
49 editor: TypographyStyle; 45 editor: TypographyStyle;
50 } 46 }
51 47
52 // eslint-disable-next-line @typescript-eslint/no-shadow -- Augment imported interface.
53 interface TypographyVariantsOptions { 48 interface TypographyVariantsOptions {
54 fontWeightEditorNormal: number; 49 fontWeightEditorNormal?: number;
55 fontWeightEditorBold: number; 50 fontWeightEditorBold?: number;
56 editor: TypographyStyle; 51 editor?: TypographyStyle;
57 } 52 }
58 53
59 interface Palette { 54 interface Palette {
@@ -62,221 +57,269 @@ declare module '@mui/material/styles' {
62 } 57 }
63 58
64 interface PaletteOptions { 59 interface PaletteOptions {
65 outer: OuterPalette; 60 outer?: Partial<OuterPalette>;
66 highlight: HighlightPalette; 61 highlight?: Partial<HighlightPalette>;
67 } 62 }
68} 63}
69 64
70const typography: TypographyVariantsOptions = { 65function createResponsiveTheme(
71 fontFamily: 66 options: ThemeOptions,
72 '"InterVariable", "Inter", "Roboto", "Helvetica", "Arial", sans-serif', 67 overrides: ThemeOptions = {},
73 fontWeightMedium: 600, 68): Theme {
74 fontWeightEditorNormal: 400, 69 const theme = createTheme({
75 fontWeightEditorBold: 700, 70 ...options,
76 button: { 71 typography: {
77 // 24px line height for 14px button text means 36px high buttons. 72 fontFamily:
78 // Making sure the button has whole pixel height reduces redering errors on Android. 73 '"InterVariable", "Inter", "Roboto", "Helvetica", "Arial", sans-serif',
79 lineHeight: 1.7143, 74 fontWeightMedium: 600,
80 }, 75 fontWeightEditorNormal: 400,
81 editor: { 76 fontWeightEditorBold: 700,
82 fontFamily: 77 button: {
83 '"JetBrains MonoVariable", "JetBrains Mono", "Cascadia Code", "Fira Code", monospace', 78 // 24px line height for 14px button text to fix browser rounding errors.
84 fontFeatureSettings: '"liga", "calt"', 79 lineHeight: 1.714286,
85 // `rem` for JetBrains MonoVariable make the text too large in Safari.
86 fontSize: '16px',
87 fontWeight: 400,
88 lineHeight: 1.5,
89 letterSpacing: 0,
90 textRendering: 'optimizeLegibility',
91 },
92};
93
94const components: Components = {
95 MuiButton: {
96 styleOverrides: {
97 root: {
98 '&.rounded': { borderRadius: '50em' },
99 '.MuiButton-startIcon': { marginRight: 6 },
100 '.MuiButton-endIcon': { marginLeft: 6 },
101 }, 80 },
102 sizeSmall: { fontSize: '0.75rem' }, 81 editor: {
103 sizeLarge: { fontSize: '1rem' }, 82 fontFamily:
104 text: { '&.rounded': { padding: '6px 14px' } }, 83 '"JetBrains MonoVariable", "JetBrains Mono", "Cascadia Code", "Fira Code", monospace',
105 textSizeSmall: { '&.rounded': { padding: '4px 8px' } }, 84 fontFeatureSettings: '"liga", "calt"',
106 textSizeLarge: { '&.rounded': { padding: '8px 20px' } }, 85 // `rem` for JetBrains MonoVariable make the text too large in Safari.
107 outlined: { '&.rounded': { padding: '5px 13px' } }, 86 fontSize: '16px',
108 outlinedSizeSmall: { '&.rounded': { padding: '3px 9px' } }, 87 fontWeight: 400,
109 outlinedSizeLarge: { '&.rounded': { padding: '7px 19px' } }, 88 lineHeight: 1.5,
110 }, 89 letterSpacing: 0,
111 }, 90 textRendering: 'optimizeLegibility',
112 MuiToggleButton: { 91 },
113 styleOverrides: { 92 ...(options.typography ?? {}),
114 root: { '&.iconOnly': { borderRadius: '100%' } },
115 }, 93 },
116 }, 94 });
117 MuiToggleButtonGroup: { 95
118 styleOverrides: { 96 function shadedButtonStyle(color: string): CSSObject {
119 root: { 97 const opacity = theme.palette.action.focusOpacity;
120 '&.rounded .MuiToggleButtonGroup-groupedHorizontal': { 98 return {
121 ':first-of-type': { 99 background: alpha(color, opacity),
122 paddingLeft: 15, 100 ':hover': {
123 borderRadius: '50em 0 0 50em', 101 background: alpha(color, opacity + theme.palette.action.hoverOpacity),
124 }, 102 '@media(hover: none)': {
125 ':last-of-type': { 103 background: alpha(color, opacity),
126 paddingRight: 15, 104 },
127 borderRadius: '0 50em 50em 0', 105 },
128 }, 106 '&.Mui-disabled': {
129 '&.MuiToggleButton-sizeSmall': { 107 background: alpha(theme.palette.text.disabled, opacity),
130 ':first-of-type': { paddingLeft: 9 }, 108 },
131 ':last-of-type': { paddingRight: 9 }, 109 };
110 }
111
112 const themeWithComponents = createTheme(theme, {
113 components: {
114 MuiCssBaseline: {
115 styleOverrides: {
116 body: {
117 overscrollBehavior: 'contain',
132 }, 118 },
133 '&.MuiToggleButton-sizeLarge': { 119 },
134 ':first-of-type': { paddingLeft: 21 }, 120 },
135 ':last-of-type': { paddingRight: 21 }, 121 MuiButton: {
122 styleOverrides: {
123 root: {
124 '&.rounded': { borderRadius: '50em' },
125 '.MuiButton-startIcon': { marginRight: 6 },
126 '.MuiButton-endIcon': { marginLeft: 6 },
127 '&.shaded': {
128 ...shadedButtonStyle(theme.palette.text.primary),
129 ...(
130 [
131 'primary',
132 'secondary',
133 'error',
134 'warning',
135 'success',
136 'info',
137 ] as const
138 ).reduce((accumulator: CSSObject, color) => {
139 const colorCapitalized =
140 (color[0] ?? '').toUpperCase() + color.substring(1);
141 return {
142 ...accumulator,
143 [`&.MuiButton-text${colorCapitalized}, &.MuiButton-outlined${colorCapitalized}`]:
144 shadedButtonStyle(theme.palette[color].main),
145 };
146 }, {}),
147 },
136 }, 148 },
149 sizeSmall: { fontSize: '0.75rem' },
150 sizeLarge: { fontSize: '1rem' },
151 text: { '&.rounded': { padding: '6px 14px' } },
152 textSizeSmall: { '&.rounded': { padding: '4px 8px' } },
153 textSizeLarge: { '&.rounded': { padding: '8px 20px' } },
154 outlined: { '&.rounded': { padding: '5px 13px' } },
155 outlinedSizeSmall: { '&.rounded': { padding: '3px 9px' } },
156 outlinedSizeLarge: { '&.rounded': { padding: '7px 19px' } },
137 }, 157 },
138 }, 158 },
139 }, 159 MuiToggleButton: {
140 }, 160 styleOverrides: {
141 MuiTooltip: { 161 root: { '&.iconOnly': { borderRadius: '100%' } },
142 styleOverrides: { 162 },
143 tooltip: {
144 background: alpha('#212121', 0.93),
145 color: '#fff',
146 }, 163 },
147 arrow: { 164 MuiToggleButtonGroup: {
148 color: alpha('#212121', 0.93), 165 styleOverrides: {
166 root: {
167 '&.rounded .MuiToggleButtonGroup-groupedHorizontal': {
168 ':first-of-type': {
169 paddingLeft: 15,
170 borderRadius: '50em 0 0 50em',
171 },
172 ':last-of-type': {
173 paddingRight: 15,
174 borderRadius: '0 50em 50em 0',
175 },
176 '&.MuiToggleButton-sizeSmall': {
177 ':first-of-type': { paddingLeft: 9 },
178 ':last-of-type': { paddingRight: 9 },
179 },
180 '&.MuiToggleButton-sizeLarge': {
181 ':first-of-type': { paddingLeft: 21 },
182 ':last-of-type': { paddingRight: 21 },
183 },
184 },
185 },
186 },
187 },
188 MuiTooltip: {
189 styleOverrides: {
190 tooltip: {
191 background: alpha('#212121', 0.93),
192 color: '#fff',
193 },
194 arrow: {
195 color: alpha('#212121', 0.93),
196 },
197 },
149 }, 198 },
150 }, 199 },
151 }, 200 });
152};
153 201
154function createResponsiveTheme(options: ThemeOptions): Theme { 202 const themeWithOverrides = createTheme(themeWithComponents, overrides);
155 return responsiveFontSizes(createTheme(options)); 203
204 return responsiveFontSizes(themeWithOverrides);
156} 205}
157 206
158const lightTheme = createResponsiveTheme({ 207const lightTheme = (() => {
159 typography, 208 const primaryText = '#19202b';
160 components, 209 const disabledText = '#a0a1a7';
161 palette: { 210 const darkBackground = '#f5f5f5';
162 mode: 'light', 211
163 primary: { main: '#038a99' }, 212 return createResponsiveTheme({
164 secondary: { main: '#e45649' }, 213 palette: {
165 error: { main: '#ca1243' }, 214 mode: 'light',
166 warning: { main: '#c18401' }, 215 primary: { main: '#038a99' },
167 success: { main: '#50a14f' }, 216 secondary: { main: '#e45649' },
168 info: { main: '#4078f2' }, 217 error: { main: '#ca1243' },
169 background: { 218 warning: { main: '#c18401' },
170 default: '#fff', 219 success: { main: '#50a14f' },
171 paper: '#fff', 220 info: { main: '#4078f2' },
172 }, 221 background: {
173 text: { 222 default: '#fff',
174 primary: '#19202b', 223 paper: '#fff',
175 secondary: '#696c77',
176 disabled: '#a0a1a7',
177 },
178 divider: alpha('#19202b', 0.16),
179 outer: {
180 background: '#f5f5f5',
181 border: '#c8c8c8',
182 },
183 highlight: {
184 cursor: '#4078f2',
185 number: '#0084bc',
186 parameter: '#6a3e3e',
187 comment: '#a0a1a7',
188 activeLine: '#f5f5f5',
189 selection: '#c8e4fb',
190 lineNumber: '#a0a1a7',
191 foldPlaceholder: alpha('#19202b', 0.08),
192 activeLintRange: alpha('#f2a60d', 0.28),
193 occurences: {
194 read: alpha('#19202b', 0.16),
195 write: alpha('#19202b', 0.16),
196 }, 224 },
197 search: { 225 text: {
198 match: '#00bcd4', 226 primary: primaryText,
199 selected: '#d500f9', 227 secondary: '#696c77',
200 contrastText: '#ffffff', 228 disabled: disabledText,
201 }, 229 },
202 }, 230 divider: alpha(primaryText, 0.16),
203 }, 231 outer: {
204}); 232 background: darkBackground,
205 233 border: '#c8c8c8',
206const darkTheme = createResponsiveTheme({ 234 },
207 typography: { 235 highlight: {
208 ...typography, 236 number: '#0084bc',
209 fontWeightEditorNormal: 350, 237 parameter: '#6a3e3e',
210 fontWeightEditorBold: 650, 238 comment: disabledText,
211 }, 239 activeLine: darkBackground,
212 components: { 240 selection: '#c8e4fb',
213 ...components, 241 foldPlaceholder: alpha(primaryText, 0.08),
214 MuiSnackbarContent: { 242 activeLintRange: alpha('#f2a60d', 0.28),
215 styleOverrides: { 243 occurences: {
216 root: { 244 read: alpha(primaryText, 0.16),
217 color: '#f00', 245 write: alpha(primaryText, 0.16),
218 backgroundColor: '#000', 246 },
219 border: `10px solid #ff0`, 247 search: {
248 match: '#00bcd4',
249 selected: '#d500f9',
250 contrastText: '#fff',
220 }, 251 },
221 }, 252 },
222 }, 253 },
223 MuiTooltip: { 254 });
224 ...(components.MuiTooltip || {}), 255})();
225 styleOverrides: { 256
226 ...(components.MuiTooltip?.styleOverrides || {}), 257const darkTheme = (() => {
227 tooltip: { 258 const primaryText = '#ebebff';
228 ...((components.MuiTooltip?.styleOverrides?.tooltip as 259 const secondaryText = '#abb2bf';
229 | CSSObject 260 const darkBackground = '#21252b';
230 | undefined) || {}), 261
231 color: '#ebebff', 262 return createResponsiveTheme(
263 {
264 typography: {
265 fontWeightEditorNormal: 350,
266 fontWeightEditorBold: 650,
267 },
268 palette: {
269 mode: 'dark',
270 primary: { main: '#56b6c2' },
271 secondary: { main: '#be5046' },
272 error: { main: '#e06c75' },
273 warning: { main: '#e5c07b' },
274 success: { main: '#98c379' },
275 info: { main: '#61afef' },
276 background: {
277 default: '#282c34',
278 paper: darkBackground,
279 },
280 text: {
281 primary: primaryText,
282 secondary: secondaryText,
283 disabled: '#5c6370',
284 },
285 divider: alpha(secondaryText, 0.24),
286 outer: {
287 background: darkBackground,
288 border: '#181a1f',
289 },
290 highlight: {
291 number: '#6188a6',
292 parameter: '#c8ae9d',
293 comment: '#7f848e',
294 activeLine: '#2c313c',
295 selection: '#404859',
296 foldPlaceholder: alpha(primaryText, 0.12),
297 activeLintRange: alpha('#fbc346', 0.28),
298 occurences: {
299 read: alpha(primaryText, 0.14),
300 write: alpha(primaryText, 0.14),
301 },
302 search: {
303 match: '#33eaff',
304 selected: '#dd33fa',
305 contrastText: darkBackground,
306 },
232 }, 307 },
233 }, 308 },
234 }, 309 },
235 }, 310 {
236 palette: { 311 components: {
237 mode: 'dark', 312 MuiTooltip: {
238 primary: { main: '#56b6c2' }, 313 styleOverrides: {
239 secondary: { main: '#be5046' }, 314 tooltip: {
240 error: { main: '#e06c75' }, 315 color: primaryText,
241 warning: { main: '#e5c07b' }, 316 },
242 success: { main: '#98c379' }, 317 },
243 info: { main: '#61afef' }, 318 },
244 background: {
245 default: '#282c34',
246 paper: '#21252b',
247 },
248 text: {
249 primary: '#ebebff',
250 secondary: '#abb2bf',
251 disabled: '#5c6370',
252 },
253 divider: alpha('#abb2bf', 0.24),
254 outer: {
255 background: '#21252b',
256 border: '#181a1f',
257 },
258 highlight: {
259 cursor: '#61afef',
260 number: '#6188a6',
261 parameter: '#c8ae9d',
262 comment: '#7f848e',
263 activeLine: '#2c313c',
264 selection: '#404859',
265 lineNumber: '#5c6370',
266 foldPlaceholder: alpha('#ebebff', 0.12),
267 activeLintRange: alpha('#fbc346', 0.28),
268 occurences: {
269 read: alpha('#ebebff', 0.14),
270 write: alpha('#ebebff', 0.14),
271 },
272 search: {
273 match: '#33eaff',
274 selected: '#dd33fa',
275 contrastText: '#21252b',
276 }, 319 },
277 }, 320 },
278 }, 321 );
279}); 322})();
280 323
281const ContrastThemeContext = createContext<Theme | undefined>(undefined); 324const ContrastThemeContext = createContext<Theme | undefined>(undefined);
282 325