DEV Community

Nguyễn Minh Chí
Nguyễn Minh Chí

Posted on

How to customize colors in dark and light modes in Material UI

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
Enter fullscreen mode Exit fullscreen mode
  • Next, we will create a file named custom-themes.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;
Enter fullscreen mode Exit fullscreen mode
  • 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.
// 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;
Enter fullscreen mode Exit fullscreen mode
  • 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.
// _variables.scss
$bg-text-input: var(--palette-bg-text-input);
Enter fullscreen mode Exit fullscreen mode

--palette-bg-text-input was created by Material UI in :root

Image description

  • And now, example we want to customize background color in TextField component.
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;
Enter fullscreen mode Exit fullscreen mode
  • Oops, the background color spills over to the our helper text

Image description

  • 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

The element we want to style its background color is the element which has class="MuiInputBase-input". So in Input.module.scss file, it will look like this:

@import "src/themes/variables";

.Input {
  & :global(.MuiFormHelperText-root) {
    margin-left: 3px;
  }
  & :global(.MuiInputBase-input) {
    background-color: $bg-text-input;
  }
}
Enter fullscreen mode Exit fullscreen mode

and now in our custom TextField component:

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;
Enter fullscreen mode Exit fullscreen mode

Image description

  • 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:

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>
  )
}
Enter fullscreen mode Exit fullscreen mode
  • And now we will see the result:

Image description
Light mode

Image description
Dark mode

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

Top comments (0)