Skip to content

Commit 647e6ee

Browse files
authored
[docs] Add a guide for building extensible themes (#46896)
1 parent 292637d commit 647e6ee

File tree

7 files changed

+479
-0
lines changed

7 files changed

+479
-0
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import * as React from 'react';
2+
import Box from '@mui/material/Box';
3+
import Button from '@mui/material/Button';
4+
import { ThemeProvider, createTheme } from '@mui/material/styles';
5+
6+
/**
7+
* Branded theme: you might want to export this as a separate file
8+
*/
9+
const brandedTokens = {
10+
palette: {
11+
primary: {
12+
main: '#000000',
13+
},
14+
secondary: {
15+
main: 'rgb(229, 229, 234)',
16+
},
17+
},
18+
shape: {
19+
borderRadius: 4,
20+
},
21+
typography: {
22+
fontFamily:
23+
'var(--font-primary, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif)',
24+
},
25+
shadows: [
26+
'none',
27+
'0 1px 2px 0 rgb(0 0 0 / 0.05)',
28+
'0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
29+
'0 2px 4px 0 rgb(0 0 0 / 0.06)',
30+
'0 2px 4px -1px rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.04)',
31+
'0 3px 5px -1px rgb(0 0 0 / 0.07), 0 1px 3px -1px rgb(0 0 0 / 0.05)',
32+
'0 4px 6px -1px rgb(0 0 0 / 0.07), 0 2px 4px -1px rgb(0 0 0 / 0.05)',
33+
'0 5px 8px -2px rgb(0 0 0 / 0.08), 0 2px 4px -1px rgb(0 0 0 / 0.05)',
34+
'0 6px 10px -2px rgb(0 0 0 / 0.08), 0 3px 5px -2px rgb(0 0 0 / 0.06)',
35+
'0 8px 12px -3px rgb(0 0 0 / 0.09), 0 3px 6px -2px rgb(0 0 0 / 0.06)',
36+
'0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 7px -3px rgb(0 0 0 / 0.07)',
37+
'0 12px 18px -4px rgb(0 0 0 / 0.11), 0 5px 9px -3px rgb(0 0 0 / 0.08)',
38+
'0 15px 22px -4px rgb(0 0 0 / 0.12), 0 6px 11px -4px rgb(0 0 0 / 0.09)',
39+
'0 18px 28px -5px rgb(0 0 0 / 0.13), 0 7px 13px -4px rgb(0 0 0 / 0.1)',
40+
'0 22px 34px -6px rgb(0 0 0 / 0.14), 0 8px 16px -5px rgb(0 0 0 / 0.11)',
41+
'0 26px 40px -7px rgb(0 0 0 / 0.15), 0 10px 19px -5px rgb(0 0 0 / 0.12)',
42+
'0 31px 47px -8px rgb(0 0 0 / 0.16), 0 12px 23px -6px rgb(0 0 0 / 0.13)',
43+
'0 36px 54px -9px rgb(0 0 0 / 0.17), 0 14px 27px -7px rgb(0 0 0 / 0.14)',
44+
'0 42px 62px -10px rgb(0 0 0 / 0.18), 0 16px 31px -8px rgb(0 0 0 / 0.15)',
45+
'0 48px 70px -11px rgb(0 0 0 / 0.2), 0 18px 36px -9px rgb(0 0 0 / 0.16)',
46+
'0 54px 78px -12px rgb(0 0 0 / 0.21), 0 20px 41px -10px rgb(0 0 0 / 0.17)',
47+
'0 60px 86px -13px rgb(0 0 0 / 0.22), 0 23px 46px -11px rgb(0 0 0 / 0.18)',
48+
'0 66px 94px -14px rgb(0 0 0 / 0.23), 0 26px 52px -12px rgb(0 0 0 / 0.19)',
49+
'0 72px 102px -15px rgb(0 0 0 / 0.24), 0 29px 58px -13px rgb(0 0 0 / 0.2)',
50+
'0 58px 82px -11px rgb(0 0 0 / 0.26), 0 21px 40px -11px rgb(0 0 0 / 0.22)',
51+
],
52+
};
53+
54+
const brandedComponents = {
55+
MuiButton: {
56+
defaultProps: {
57+
disableElevation: true,
58+
},
59+
styleOverrides: {
60+
root: ({ theme }) => ({
61+
minWidth: 'unset',
62+
textTransform: 'capitalize',
63+
fontSize: '1rem',
64+
'&:hover': {
65+
textDecoration: 'underline',
66+
},
67+
[theme.breakpoints.up('md')]: {
68+
fontSize: '0.875rem',
69+
},
70+
}),
71+
},
72+
},
73+
};
74+
75+
const brandedTheme = createTheme({
76+
...brandedTokens,
77+
components: brandedComponents,
78+
});
79+
80+
/**
81+
* Application theme
82+
*/
83+
const appTheme = createTheme({
84+
...brandedTokens,
85+
palette: {
86+
...brandedTokens.palette,
87+
primary: {
88+
main: '#1976d2',
89+
},
90+
},
91+
components: {
92+
...brandedComponents,
93+
MuiButton: {
94+
styleOverrides: {
95+
root: [
96+
brandedComponents?.MuiButton?.styleOverrides?.root,
97+
{
98+
transition: 'transform 0.2s ease-in-out',
99+
'&:hover': {
100+
transform: 'translateY(-2px)',
101+
},
102+
},
103+
],
104+
},
105+
},
106+
},
107+
});
108+
109+
function App1() {
110+
return (
111+
<ThemeProvider theme={appTheme}>
112+
<Button>App Button</Button>
113+
</ThemeProvider>
114+
);
115+
}
116+
117+
const appTheme2 = createTheme({
118+
...brandedTokens,
119+
palette: {
120+
...brandedTokens.palette,
121+
primary: {
122+
main: '#ffa726',
123+
},
124+
},
125+
components: {
126+
...brandedComponents,
127+
MuiButton: {
128+
defaultProps: {
129+
...brandedComponents?.MuiButton?.defaultProps,
130+
variant: 'outlined',
131+
},
132+
styleOverrides: {
133+
root: [
134+
brandedComponents?.MuiButton?.styleOverrides?.root,
135+
({ theme }) => ({
136+
color: theme.palette.primary.dark,
137+
}),
138+
],
139+
},
140+
},
141+
},
142+
});
143+
144+
function App2() {
145+
return (
146+
<ThemeProvider theme={appTheme2}>
147+
<Button>App 2 Button</Button>
148+
</ThemeProvider>
149+
);
150+
}
151+
152+
export default function ExtensibleThemes() {
153+
return (
154+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
155+
<ThemeProvider theme={brandedTheme}>
156+
<Button>Branded Button</Button>
157+
</ThemeProvider>
158+
<App1 />
159+
<App2 />
160+
</Box>
161+
);
162+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import * as React from 'react';
2+
import Box from '@mui/material/Box';
3+
import Button from '@mui/material/Button';
4+
import { ThemeProvider, createTheme, type ThemeOptions } from '@mui/material/styles';
5+
6+
/**
7+
* Branded theme: you might want to export this as a separate file
8+
*/
9+
const brandedTokens: ThemeOptions = {
10+
palette: {
11+
primary: {
12+
main: '#000000',
13+
},
14+
secondary: {
15+
main: 'rgb(229, 229, 234)',
16+
},
17+
},
18+
shape: {
19+
borderRadius: 4,
20+
},
21+
typography: {
22+
fontFamily:
23+
'var(--font-primary, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif)',
24+
},
25+
shadows: [
26+
'none',
27+
'0 1px 2px 0 rgb(0 0 0 / 0.05)',
28+
'0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
29+
'0 2px 4px 0 rgb(0 0 0 / 0.06)',
30+
'0 2px 4px -1px rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.04)',
31+
'0 3px 5px -1px rgb(0 0 0 / 0.07), 0 1px 3px -1px rgb(0 0 0 / 0.05)',
32+
'0 4px 6px -1px rgb(0 0 0 / 0.07), 0 2px 4px -1px rgb(0 0 0 / 0.05)',
33+
'0 5px 8px -2px rgb(0 0 0 / 0.08), 0 2px 4px -1px rgb(0 0 0 / 0.05)',
34+
'0 6px 10px -2px rgb(0 0 0 / 0.08), 0 3px 5px -2px rgb(0 0 0 / 0.06)',
35+
'0 8px 12px -3px rgb(0 0 0 / 0.09), 0 3px 6px -2px rgb(0 0 0 / 0.06)',
36+
'0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 7px -3px rgb(0 0 0 / 0.07)',
37+
'0 12px 18px -4px rgb(0 0 0 / 0.11), 0 5px 9px -3px rgb(0 0 0 / 0.08)',
38+
'0 15px 22px -4px rgb(0 0 0 / 0.12), 0 6px 11px -4px rgb(0 0 0 / 0.09)',
39+
'0 18px 28px -5px rgb(0 0 0 / 0.13), 0 7px 13px -4px rgb(0 0 0 / 0.1)',
40+
'0 22px 34px -6px rgb(0 0 0 / 0.14), 0 8px 16px -5px rgb(0 0 0 / 0.11)',
41+
'0 26px 40px -7px rgb(0 0 0 / 0.15), 0 10px 19px -5px rgb(0 0 0 / 0.12)',
42+
'0 31px 47px -8px rgb(0 0 0 / 0.16), 0 12px 23px -6px rgb(0 0 0 / 0.13)',
43+
'0 36px 54px -9px rgb(0 0 0 / 0.17), 0 14px 27px -7px rgb(0 0 0 / 0.14)',
44+
'0 42px 62px -10px rgb(0 0 0 / 0.18), 0 16px 31px -8px rgb(0 0 0 / 0.15)',
45+
'0 48px 70px -11px rgb(0 0 0 / 0.2), 0 18px 36px -9px rgb(0 0 0 / 0.16)',
46+
'0 54px 78px -12px rgb(0 0 0 / 0.21), 0 20px 41px -10px rgb(0 0 0 / 0.17)',
47+
'0 60px 86px -13px rgb(0 0 0 / 0.22), 0 23px 46px -11px rgb(0 0 0 / 0.18)',
48+
'0 66px 94px -14px rgb(0 0 0 / 0.23), 0 26px 52px -12px rgb(0 0 0 / 0.19)',
49+
'0 72px 102px -15px rgb(0 0 0 / 0.24), 0 29px 58px -13px rgb(0 0 0 / 0.2)',
50+
'0 58px 82px -11px rgb(0 0 0 / 0.26), 0 21px 40px -11px rgb(0 0 0 / 0.22)',
51+
],
52+
};
53+
54+
const brandedComponents: ThemeOptions['components'] = {
55+
MuiButton: {
56+
defaultProps: {
57+
disableElevation: true,
58+
},
59+
styleOverrides: {
60+
root: ({ theme }) => ({
61+
minWidth: 'unset',
62+
textTransform: 'capitalize',
63+
fontSize: '1rem',
64+
'&:hover': {
65+
textDecoration: 'underline',
66+
},
67+
[theme.breakpoints.up('md')]: {
68+
fontSize: '0.875rem',
69+
},
70+
}),
71+
},
72+
},
73+
};
74+
75+
const brandedTheme = createTheme({
76+
...brandedTokens,
77+
components: brandedComponents,
78+
});
79+
80+
/**
81+
* Application theme
82+
*/
83+
const appTheme = createTheme({
84+
...brandedTokens,
85+
palette: {
86+
...brandedTokens.palette,
87+
primary: {
88+
main: '#1976d2',
89+
},
90+
},
91+
components: {
92+
...brandedComponents,
93+
MuiButton: {
94+
styleOverrides: {
95+
root: [
96+
brandedComponents?.MuiButton?.styleOverrides?.root,
97+
{
98+
transition: 'transform 0.2s ease-in-out',
99+
'&:hover': {
100+
transform: 'translateY(-2px)',
101+
},
102+
},
103+
],
104+
},
105+
},
106+
},
107+
});
108+
109+
function App1() {
110+
return (
111+
<ThemeProvider theme={appTheme}>
112+
<Button>App Button</Button>
113+
</ThemeProvider>
114+
);
115+
}
116+
117+
const appTheme2 = createTheme({
118+
...brandedTokens,
119+
palette: {
120+
...brandedTokens.palette,
121+
primary: {
122+
main: '#ffa726',
123+
},
124+
},
125+
components: {
126+
...brandedComponents,
127+
MuiButton: {
128+
defaultProps: {
129+
...brandedComponents?.MuiButton?.defaultProps,
130+
variant: 'outlined',
131+
},
132+
styleOverrides: {
133+
root: [
134+
brandedComponents?.MuiButton?.styleOverrides?.root,
135+
({ theme }) => ({
136+
color: theme.palette.primary.dark,
137+
}),
138+
],
139+
},
140+
},
141+
},
142+
});
143+
144+
function App2() {
145+
return (
146+
<ThemeProvider theme={appTheme2}>
147+
<Button>App 2 Button</Button>
148+
</ThemeProvider>
149+
);
150+
}
151+
152+
export default function ExtensibleThemes() {
153+
return (
154+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
155+
<ThemeProvider theme={brandedTheme}>
156+
<Button>Branded Button</Button>
157+
</ThemeProvider>
158+
<App1 />
159+
<App2 />
160+
</Box>
161+
);
162+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<ThemeProvider theme={brandedTheme}>
2+
<Button>Branded Button</Button>
3+
</ThemeProvider>
4+
<App1 />
5+
<App2 />

0 commit comments

Comments
 (0)