mui / mui/material-ui

[mui-6] CssVarsProvider createTheme palette.mode ignores existing colorSchemes

Open
#43,996 5 comments 0 reactions 1 assignee View on GitHub

@siriwatknp is already working on this.

Since Oct 5, 2024.

customization: theme scope: all components type: expected behavior
Dominant language
JavaScript
Stars
99.1k
Forks
32.5k
Avg merge
2d 17h
Merged PRs (30d)
106

Description

Summary

I would like createTheme to use existing colorScheme definitions when initialized with a mode and simply apply them to the theme, as opposed to generating a default and applying it to the theme. I'd like this to only generate a default is a suitable colorScheme had not already been provided.

Examples

My Theme looks something like this:

{
  "name": "Theme Example",
  "colorSchemes": {
    "light": {
      "palette": {
        "primary": {
          "light": "var(--ns-palette-light-primary-light)",
          "main": "var(--ns-palette-light-primary-main)",
          "dark": "var(--ns-palette-light-primary-dark)",
          "lightChannel": "var(--ns-palette-light-primary-light-channel)",
          "mainChannel": "var(--ns-palette-light-primary-main-channel)",
          "darkChannel": "var(--ns-palette-light-primary-dark-channel)",
          "contrastText": "var(--ns-palette-light-primary-contrast-text)"
        },
...
      }
    },
    "dark": {
      "palette": {
        "primary": {
          "light": "var(--ns-palette-dark-primary-light)",
          "main": "var(--ns-palette-dark-primary-main)",
          "dark": "var(--ns-palette-dark-primary-dark)",
          "lightChannel": "var(--ns-palette-dark-primary-light-channel)",
          "mainChannel": "var(--ns-palette-dark-primary-main-channel)",
          "darkChannel": "var(--ns-palette-dark-primary-dark-channel)",
          "contrastText": "var(--ns-palette-dark-primary-contrast-text)"
        },
...
    }
  },
...
  "cssVariables": {
    "colorSchemeSelector": "class"
  },
  "palette": {}
}

This is heavily trimmed, but it should give the context. And yes, the theme itself pulls in css variables for the underlying primitives from our design system, and whilst it gets somewhat indirect, it actually works very nicely.

Then I change color scheme to dark, the main theme (the one attached to the ThemeProvider), becomes:

{
  "defaultColorScheme": "light",
  "name": "Dynamic Theme",
  "colorSchemeSelector": "class",
  "rootSelector": ":root",
  "vars": {
    "palette": {
      "primary": {
        "light": "var(--mui-palette-primary-light)",
        "main": "var(--mui-palette-primary-main)",
        "dark": "var(--mui-palette-primary-dark)",
        "lightChannel": "var(--mui-palette-primary-lightChannel)",
        "mainChannel": "var(--mui-palette-primary-mainChannel)",
        "darkChannel": "var(--mui-palette-primary-darkChannel)",
        "contrastText": "var(--mui-palette-primary-contrastText)",
        "contrastTextChannel": "var(--mui-palette-primary-contrastTextChannel)"
      },
...
    },
...
  },
  "palette": {
    "mode": "dark",
    "primary": {
      "light": "var(--ns-palette-dark-primary-light)",
      "main": "var(--ns-palette-dark-primary-main)",
      "dark": "var(--ns-palette-dark-primary-dark)",
      "lightChannel": "var(--ns-palette-dark-primary-light-channel)",
      "mainChannel": "var(--ns-palette-dark-primary-main-channel)",
      "darkChannel": "var(--ns-palette-dark-primary-dark-channel)",
      "contrastText": "var(--ns-palette-dark-primary-contrast-text)",
      "contrastTextChannel": "var(--ns-palette-dark-primary-contrast-text)"
    },
...
  "colorSchemes": {
    "light": {
      "palette": {
        "mode": "light",
        "primary": {
          "light": "var(--ns-palette-light-primary-light)",
          "main": "var(--ns-palette-light-primary-main)",
          "dark": "var(--ns-palette-light-primary-dark)",
          "lightChannel": "var(--ns-palette-light-primary-light-channel)",
          "mainChannel": "var(--ns-palette-light-primary-main-channel)",
          "darkChannel": "var(--ns-palette-light-primary-dark-channel)",
          "contrastText": "var(--ns-palette-light-primary-contrast-text)",
          "contrastTextChannel": "var(--ns-palette-light-primary-contrast-text)"
        },
...
    },
    "dark": {
      "palette": {
        "mode": "dark",
        "primary": {
          "light": "var(--ns-palette-dark-primary-light)",
          "main": "var(--ns-palette-dark-primary-main)",
          "dark": "var(--ns-palette-dark-primary-dark)",
          "lightChannel": "var(--ns-palette-dark-primary-light-channel)",
          "mainChannel": "var(--ns-palette-dark-primary-main-channel)",
          "darkChannel": "var(--ns-palette-dark-primary-dark-channel)",
          "contrastText": "var(--ns-palette-dark-primary-contrast-text)",
          "contrastTextChannel": "var(--ns-palette-dark-primary-contrast-text)"
        },
...
  },
  "cssVarPrefix": "mui"
}

With palette switching to dark as desired and referencing the underlying colorScheme.

If I just pass in mode = 'dark' to create them, I get this instead:

{
  "defaultColorScheme": "dark",
  "name": "Theme Example",
  "colorSchemeSelector": "class",
  "rootSelector": ":root",
  "vars": {
    "palette": {
      "primary": {
        "light": "var(--mui-palette-primary-light, #e3f2fd)",
        "main": "var(--mui-palette-primary-main, #90caf9)",
        "dark": "var(--mui-palette-primary-dark, #42a5f5)",
        "lightChannel": "var(--mui-palette-primary-lightChannel, 227 242 253)",
        "mainChannel": "var(--mui-palette-primary-mainChannel, 144 202 249)",
        "darkChannel": "var(--mui-palette-primary-darkChannel, 66 165 245)",
        "contrastText": "var(--mui-palette-primary-contrastText, rgba(0, 0, 0, 0.87))",
        "contrastTextChannel": "var(--mui-palette-primary-contrastTextChannel, 0 0 0)"
      },
...
    },
  },
  "palette": {
    "mode": "dark",
    "primary": {
      "main": "#90caf9",
      "light": "#e3f2fd",
      "dark": "#42a5f5",
      "contrastText": "rgba(0, 0, 0, 0.87)",
      "mainChannel": "144 202 249",
      "lightChannel": "227 242 253",
      "darkChannel": "66 165 245",
      "contrastTextChannel": "0 0 0"
    },
...
  "colorSchemes": {
    "dark": {
      "palette": {
        "mode": "dark",
        "primary": {
          "main": "#90caf9",
          "light": "#e3f2fd",
          "dark": "#42a5f5",
          "contrastText": "rgba(0, 0, 0, 0.87)",
          "mainChannel": "144 202 249",
          "lightChannel": "227 242 253",
          "darkChannel": "66 165 245",
          "contrastTextChannel": "0 0 0"
        },
...
    },
    "light": {
      "palette": {
        "mode": "light",
        "primary": {
          "light": "var(--ns-palette-light-primary-light)",
          "main": "var(--ns-palette-light-primary-main)",
          "dark": "var(--ns-palette-light-primary-dark)",
          "lightChannel": "var(--ns-palette-light-primary-light-channel)",
          "mainChannel": "var(--ns-palette-light-primary-main-channel)",
          "darkChannel": "var(--ns-palette-light-primary-dark-channel)",
          "contrastText": "var(--ns-palette-light-primary-contrast-text)",
          "contrastTextChannel": "var(--ns-palette-light-primary-contrast-text)"
        },
...
  },
  "cssVarPrefix": "mui",
}

Whatever theme I pass in as the mode gets overwritten.

Now, I do understand that this is [probably intentional, but it would be awfully useful if this overwriting of the colorScheme only happened when there wasn't already a colorScheme defined. That would make this much more flexible, and to my mind at least, more consistent

Motivation

I'm trying to display a theme inspector - similar to the mui 6 one, where I create a theme using cssVariables, and I can toggle between light and dark modes of the theme without changing the theme that's applied to the application.

I want some way to create a theme object that can be inspected in both light and dark modes that isn't tied to the DOM, as far as I can tell from digging into useCurrentColorScheme.js, there's no way to get a theme in a specific mode without it trying to edit the DOM. I want to be able to inspect the css variables, not apply them.

The closest is to use createTheme with palette.mode set to either light or dark, however that also initializes a default color scheme for that mode ignoring anything that you might already have set and then applying that scheme to the palette. This works as desired if you are using an autogenerated theme, but if you are trying to migrate from an existing theme and have a rather large set of values that you want to preserve, it ignores and overwrites them.

If I just display the main application theme, I can see it switch between light and dark mode and update the palette and it's working beautifully, I can inspect that value and get exactly the behavior that I'm after, but it's changing the application theme.

I want the same change to a theme but without tying it to a ThemeProvider or having it try and update the DOM.

Search keywords: CssVarProvider

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.