13

To customize my Material UI palette, I've done something like this to set up my theme:

import blue from '@material-ui/core/colors/blue';

const theme = createMuiTheme({
  palette: {
    primary: blue,
  },
});

blue contains many shades, as shown below:

enter image description here

Now how do I access all the shades of primary palette inside of my component? Accessing all the shades of palette.grey is easy as shown below:

enter image description here

But I can't seem to access the shades of primary other than main, dark, light, and contrastColor, as shown below:

enter image description here

How do I access all the shades (e.g. theme.palette.primary.A100) from my component?

Johnny Oshika
  • 54,741
  • 40
  • 181
  • 275

2 Answers2

9

Now how do I access all the shades of primary palette inside of my component?

I think there is a misunderstanding here. You can already access variants of the theme color (dark, light, contrastText), but if what you mean is all shades from the color object (blue[100], blue[200],...). Then no, createTheme() does not generate that for you automatically.

From Material-UI docs:

Only the main shades need be provided (unless you wish to further customize light, dark or contrastText), as the other colors will be calculated by createTheme(), as described in the Theme customization section.

When you supply only 1 color to the main property like below:

const theme = createTheme({
  palette: {
    primary: {
      main: "#607d8b"
    }
  }
});

Material-UI will calculate the light, dark variants of that color along with contrastText for the text color. You can log the theme.palette.main to confirm this:

const useStyles = makeStyles((theme) => {
  console.log(theme.palette.main);
  return {};
});
{
  main: "#607d8b",
  light: "rgb(127, 151, 162)",
  dark: "rgb(67, 87, 97)",
  contrastText: "#fff",
}

If you however decide to pass a color object:

import { createTheme } from '@material-ui/core/styles';
import blue from '@material-ui/core/colors/blue';

const theme = createTheme({
  palette: {
    primary: blue,
  },
});

Then Material-UI will find the main shade of that object. Default is blue[500], and calculate the dark, light and contrastText based on the main color as normal. The end result will look like this:

const useStyles = makeStyles((theme) => {
  console.log(theme.palette.main);
  return {};
});
{
  50: "#eceff1",
  100: "#cfd8dc",
  200: "#b0bec5",
  300: "#90a4ae",
  400: "#78909c",
  500: "#607d8b", // createPalette() uses this color as main
  600: "#546e7a",
  700: "#455a64",
  800: "#37474f",
  900: "#263238",
  A100: "#cfd8dc",
  A200: "#b0bec5",
  A400: "#78909c",
  A700: "#455a64",
  main: "#607d8b", // added from createPalette()
  light: "#90a4ae", // added from createPalette()
  dark: "#455a64", // added from createPalette()
  contrastText: "#fff", // added from createPalette()
}

Typescript Usage

As this issue points out, if you are using Typescript, you may realize that it does not give you the shades information in the theme PaletteColor.

const useStyles = makeStyles((theme) => {
  const shade = theme.palette.main[500]; // shade is any instead of string
  return {};
});

The solution as the maintainer suggested is to use module augmentation to extend the PaletteColor definition:

import { ColorPartial } from "@material-ui/core/styles/createPalette";

declare module "@material-ui/core/styles/createPalette" {
  interface PaletteColor extends ColorPartial {}
}

If you are using this linter rule suggestion to avoid importing private modules (anything deeper than 2 level imports). You can create ColorPartial yourself easily like this:

import { Color } from "@material-ui/core";

type ColorPartial = Partial<Color>;

declare module "@material-ui/core/styles/createPalette" {
  interface PaletteColor extends ColorPartial {}
}

Now typescript is aware of all possible shades in your PaletteColor:

const useStyles = makeStyles((theme) => {
  const shade = theme.palette.main[500]; // shade is string
  return {};
});

Live Demo

Edit 67013112/material-ui-how-to-access-all-palette-shades-inside-component

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
  • Very nice, works really well! It turns out that it was TypeScript that was throwing me off. I can only get this to work if I add `//@ts-ignore` on top of `theme.palette.primary[500]`, otherwise I get this error: `Element implicitly has an 'any' type because expression of type '500' can't be used to index type 'PaletteColor'. TS7053`. Your codesandbox doesn't seem to have that problem. How did you get TypeScript to allow the `500` indexer on `PaletteColor`? – Johnny Oshika Apr 09 '21 at 14:07
  • Here's my sandbox. I can get it to work, but the code editor shows type errors. In VS Code, it won't even run unless I add `//@ts-ignore`. https://codesandbox.io/s/material-ui-access-all-palette-shades-inside-components-gimgx?file=/src/demo.tsx – Johnny Oshika Apr 09 '21 at 14:27
  • 1
    @JohnnyOshika I just copied the tsconfig in my working sandbox to yours and it still throws error. Not sure why mine works because it's [not supposed to](https://github.com/mui-org/material-ui/issues/20995#issuecomment-626904458). One solution is to use module augmentation to extend `PaletteColor`. See [this](https://material-ui.com/customization/palette/#adding-new-colors) example. Hit me up if you need an example of `PaletteColor` extention. – NearHuscarl Apr 09 '21 at 14:36
  • @NearHuscari I tried module augmentation. While it works with custom palette names (e.g. `primaryShades` is what I'm using), it doesn't when I want to augment a standard name like `primary` as `Color`, as it's already defined as `PaletteColor`. The error I get is this: `Subsequent property declarations must have the same type. Property 'primary' must be of type 'PaletteColor', but here has type 'Color'.ts(2717)` – Johnny Oshika Apr 09 '21 at 16:02
  • My working solution is this custom palette that I create (`primaryShades`), which works well enough. It would be nice to use the default `primary`, however. – Johnny Oshika Apr 09 '21 at 16:04
  • @JohnnyOshika You don't really need `primaryShades`. I made a typescript example for you. – NearHuscarl Apr 10 '21 at 01:15
  • @NearHuscari Well that's awesome! I'll delete my answer. Many thanks. – Johnny Oshika Apr 10 '21 at 04:44
0

According to the Material UI color systems docs, it automatically calculates several values, such as dark, light, main. However, for more variant, it says using color objects such as @material-ui/core/colors/blue. it is maybe the blue you used in your first example code.

Color objects have a structure like below. github link

const blue = {
  50: '#e3f2fd',
  100: '#bbdefb',
  200: '#90caf9',
  300: '#64b5f6',
  400: '#42a5f5',
  500: '#2196f3',
  600: '#1e88e5',
  700: '#1976d2',
  800: '#1565c0',
  900: '#0d47a1',
  A100: '#82b1ff',
  A200: '#448aff',
  A400: '#2979ff',
  A700: '#2962ff',
};

export default blue;

So if you want to use more variants for colors that are not served as color object, you should make color variance youreserlf.

Hyeonseo Kim
  • 324
  • 3
  • 15
  • Thanks, but how do I access those different shades of blue in my React components? The theme gets injected to `makeStyles` but I can't seem to access the shades of primary color there. Are you suggesting I hard code those values in a global `blue` module and import that module? That would be bypassing the Material UI theme. – Johnny Oshika Apr 09 '21 at 00:09
  • I'm not sure but I think you should make a function like `createPalette` or override this. [source code](https://github.com/mui-org/material-ui/blob/661e595db02628a247bb8e529fb98aa92c0bb3b2/packages/material-ui/src/styles/createPalette.js) – Hyeonseo Kim Apr 09 '21 at 00:22