How to customize colors in dark and light modes in Material UI
Tags: materialui, react
Published: 2023-10-07 16:34:17

When we use UI libraries like Material UI in our React project, sometimes we need to customize color in both dark and light modes instead of using its default colors. This post will instruct you how to do that.

- First, make sure you have installed Material UI if you have not installed it yet, let's type:
```
npm install @mui/material @emotion/react @emotion/styled
```
- Next, we will create a file named `custom-themes.ts`:
```ts
// custom-themes.ts
import { experimental_extendTheme as extendTheme } from "@mui/material/styles";

//If you are using TypeScript, you must declare your custom color's name in both PaletteOptions and Palette in module "@mui/material/styles"
declare module "@mui/material/styles" {
  interface PaletteOptions {
    "bg-text-input": string;
  }
  interface Palette {
    "bg-text-input": string;
  }
}

const customTheme = extendTheme({
  cssVarPrefix: "",
  colorSchemes: {
    light: {
      palette: {
        "bg-text-input": "#C0C0C0", // Specify color in light mode for "bg-text-input"
      },
    },
    dark: {
      palette: {
        "bg-text-input": "#0f1a2a", // Specify color in dark mode for "bg-text-input"
      },
    },
  },
});

export default customTheme;
```
- In `App.tsx` file, we wrap `<CssVarsProvider />` component at the top. Then, we have to add the `theme` property in this component, it will receive `customTheme`, which we have declared above as a value. And don't forget to add `<CssBaseline />` because without it, the dark mode will not work.
```tsx
// App.tsx
import { Box, CssBaseline } from "@mui/material";
import { Experimental_CssVarsProvider as CssVarsProvider } from "@mui/material/styles";

import Home from "./pages/Home";
import customTheme from "./themes/custom-themes";

const App = () => {
  return (
    <CssVarsProvider theme={customTheme}>
      <CssBaseline />
      <Box
        sx={{
          minHeight: "100vh",
          width: "100%",
          maxWidth: "1200px",
          margin: "auto",
          paddingTop: 10,
          paddingLeft: 30,
          paddingRight: 30,
          paddingBottom: 30,
        }}
      >
        <Home />
      </Box>
    </CssVarsProvider>
  );
};

export default App;
```
- We also create a file named `_variables.scss`. This file will redeclare all our custom colors that we have declared in `custom-themes.ts` file. The purpose of doing this is so that we can use our custom colors in scss. It's convenient for customizing nested element in Material UI, which we can not style it directly. I will show you clearly below.
```scss
// _variables.scss
$bg-text-input: var(--palette-bg-text-input);
```
> `--palette-bg-text-input` was created by Material UI in `:root`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xwnmcyyvx2ec8xsrz5ha.png)


- And now, example we want to customize background color in TextField component.
```tsx
import React from "react";
import { TextField, TextFieldProps } from "@mui/material";

type TInputProps = TextFieldProps & {};

const Input = React.forwardRef<HTMLInputElement, TInputProps>(
  ({ ...otherProps }, ref) => {
    return (
      <TextField
        size="small"
        fullWidth
        ref={ref}
        {...otherProps}
        sx={{backgroundColor: (theme) => theme.palette["bg-text-input"]}}
      />
    );
  }
);

export default Input;
```
- Oops, the background color spills over to the our helper text

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06iv904iq2vsh8hf8xg0.png)
- That why we need to create a `.scss` file to style nested element. First, we need to press F12 to see exact element that we want to style it.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12lzfensn6xk7r0w6w35.png)

The element we want to style its background color is the <input/> element which has class="MuiInputBase-input". So in `Input.module.scss` file, it will look like this:
```scss
@import "src/themes/variables";

.Input {
  & :global(.MuiFormHelperText-root) {
    margin-left: 3px;
  }
  & :global(.MuiInputBase-input) {
    background-color: $bg-text-input;
  }
}
```
and now in our custom TextField component:
```tsx
import React from "react";
import { TextField, TextFieldProps } from "@mui/material";

import styles from "./Input.module.scss";

type TInputProps = TextFieldProps & {};

const Input = React.forwardRef<HTMLInputElement, TInputProps>(
  ({ ...otherProps }, ref) => {
    return (
      <TextField
        size="small"
        fullWidth
        ref={ref}
        {...otherProps}
        className={styles.Input} // Specify the wrapper class for this component
      />
    );
  }
);

export default Input;
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b98926u0i869bwgy9kgc.png)
- Now it looks better.

- Finally, we will create a button to switch between light and dark mode. We use a hook provided by Material UI called `useColorScheme`:
```tsx
import { Button, useColorScheme } from "@mui/material";

const ToggleModeButton = () => {
  const { mode, setMode } = useColorScheme();
  return (
    <Button
        variant="contained"
        sx={{ mt: 2, textTransform: "none" }}
        onClick={() => {
          setMode(mode === "light" ? "dark" : "light");
        }}
      >
        Toggle mode
      </Button>
  )
}
```
- And now we will see the result:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qctv9kxbgdj9857v1qpp.png)
                    Light mode

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkgpo373s5v21hgqeeou.png)
                     Dark mode

- Thanks for reading here. Hope this post will help you <3.