mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
94.11k stars 32.35k forks source link

Inventory of how to customize the global theme in v5 #32150

Open EdmundsEcho opened 2 years ago

EdmundsEcho commented 2 years ago

Duplicates

Latest version

Current docs

The docs for v5 are really good. In the theme customization section I was taking inventory of how to customize the theme used throughout the app. While doing so, I was not able to fully understand the capability in one or two spots. If it's not just my missing something, I thought it might be worthwhile to clarify in the docs accordingly.

Scope of how to create a theme that is inherently responsive

Here is the summary of how the "theme" as "function that generates a theme", we have the following options enabled by the mui createTheme engineering:

createTheme takes the following value that has "built-in" capacity to respond:

components: {
    MuiComponent: {
        defaultProps: {
            'mui-propName': value
        },
        styleOverrides: { 
            'mui-slot': Object  // { jsCssProps } 
                      | Function ({ ownerState, theme }) => {..} // return { jsCssProps }
                      | sx({ sxObject }) // requires experimental_sx, mui uses @emotion to interpret
                      | `css` // js string literal that complies with css syntax; access to global theme?
        },
        variants : [
            {
                props: { variant: `nameOfVariant`, ...otherMuiProps }, // ~v4 default props, variant is required
                style: { keyValue }, // css? or jsCss?; does the value in { key:value } has access to global theme?
            }
        ]
    }
}

The above accomplishes:

"dynamic" comes from changing `className` ```js mui default theme -> refs that match mui components/slots present in our theme -> component specific style ``` The styles are applied when the application of the css selector expression returns the component/node being rendered. The dynamics comes from the ability to change the `className` values ("for free" when the browser dynamically switches `pseudo-element`s).

Efficiently specify different versions of related components by way of code reuse that that comes with using a function and parameter combination.

The open questions

  1. As I ask the question, I suspect the answer is obvious, but I wanted to clarify. Access the theme value:

    • When createTheme encounters styleOverrides: String does it create a closure with access to the theme value? So, can we set the value of mui-slot: color: ${theme.palette.primary.main}?
    • Similarly, when createTheme encounters variants, does it interpret the style value in a closure with access to the theme value?
  2. Given that the place to call createTheme is at the root of the app by way of

    <ThemeProvider theme={createTheme(myInherentlyDynamicTheme)}><App /></ThemeProvider>`

    in the event I am using CssBaseline, what is the advantage of creating myInherentlyDynamicTheme using the CssBaseline overrides I saw documented?

  3. Given the now defacto demand for a UI that toggles between light and dark themes, from within the App:

    • I can read the theme value using the useTheme() hook from '@mui/material/styles', but
    • there is no-way to create a new theme to be applied to the App globally; in other words, the question is what prevents '@mui/material/styles' from providing access to a setTheme function?
    • the "work-around" is to wrap the <ThemeProvider /> in a context with a theme that depends on the extra context to create another layer of responsiveness. Yes?

Is there no way to change ThemeProvider's internal ref to theme?

If not, this inability to call a setTheme directly on ThemeProvider limits the scope of the dynamics of the theme. This limit is the reason for documented approach where we roll-our-own Context that hosts [mode, setMode]? (a context that is a parent to ThemeProvider). Is this right?

Thank you in advance to confirming/clarifying.

mnajdova commented 2 years ago

When createTheme encounters styleOverrides: String does it create a closure with access to the theme value? So, can we set the value of mui-slot: color: ${theme.palette.primary.main}?

Yes, here is a reference: https://mui.com/material-ui/customization/theme-components/#overrides-based-on-props

Similarly, when createTheme encounters variants, does it interpret the style value in a closure with access to the theme value?

Yes, but you have only access to the theme, as the props are already specified in the props key. My recommendation would be using styleOverrides where you specify the slot and then use style function. It's very likely we are going to land on this API only in some of the future versions.

Given that the place to call createTheme is at the root of the app by way of

<ThemeProvider theme={createTheme(myInherentlyDynamicTheme)}><App /></ThemeProvider>

in the event I am using CssBaseline, what is the advantage of creating myInherentlyDynamicTheme using the CssBaseline overrides I saw documented?

Can you share the docs you were looking? It's not clear what the question is here.

Given the now defacto demand for a UI that toggles between light and dark themes, from within the App: I can read the theme value using the useTheme() hook from '@mui/material/styles', but there is no-way to create a new theme to be applied to the App globally; in other words, the question is what prevents '@mui/material/styles' from providing access to a setTheme function? the "work-around" is to wrap the in a context with a theme that depends on the extra context to create another layer of responsiveness. Yes?

Hopefully this can help for setting up dark mode - https://mui.com/material-ui/customization/dark-mode/#dark-mode-with-a-custom-palette

Is there no way to change ThemeProvider's internal ref to theme? If not, this inability to call a setTheme directly on ThemeProvider limits the scope of the dynamics of the theme. This limit is the reason for documented approach where we roll-our-own Context that hosts [mode, setMode]? (a context that is a parent to ThemeProvider). Is this right?

Hopefully the previous guide can help.