본문 바로가기

Frontend/React&RN

React + TS + Styled Component + Material UI + Dark Theme

이번에 회사에서 프로젝트를 하면서 Material UI를 사용하였는데,
Material UI에서 제공하는 기본 스타일링 문법이 마음에 들지 않았다.

아예 scss도 아니고 Styled Component도 아닌 좀 애매한 문법이라는 생각이 들어서 Styled Component에서 Material UI의 기본 기능들을 사용할 수 있게 세팅해보았다.

  • 원하는 컬러를 @material-ui/core/colors에서 꺼내서 palette 생성
  • 다크모드시 컬러 팔레트의 속성을 반전시킬 수 있도록 설정
// colors.ts
import { createMuiTheme } from "@material-ui/core/styles";
import green from "@material-ui/core/colors/green";
import teal from "@material-ui/core/colors/teal";

const palette = {
  primary: {
    light: teal[400],
    main: teal[600],
    dark: teal[800],
    contrastText: "#fff",
  },
  secondary: {
    light: green[200],
    main: green[400],
    dark: green[600],
    contrastText: "#fff",
  },
};

export const createTheme = (prefersDarkMode: boolean) =>
  createMuiTheme({
    palette: {
      type: prefersDarkMode ? "dark" : "light",
      ...palette,
    },
  });
  • styled components의 ThemeProvider에 다크모드 처리한 theme 삽입
// ColorProvider.tsx
import React from "react";
import { DefaultTheme, ThemeProvider } from "styled-components";

type ColorProviderProps = {
  children: React.ReactNode;
  theme: DefaultTheme;
};

function ColorProvider({ children, theme }: ColorProviderProps) {
  return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
}

export default ColorProvider;
  • material ui의 ThemeProvider와 styled components의 ThemeProvider인 ColorProvider를 중첩해서 적용
  • material ui의 useMediaQuery 훅으로 다크테마 감지
// App.tsx
import React, { useMemo } from "react";
import "./assets/css/index.scss";
import {
  ThemeProvider,
  StylesProvider,
  useMediaQuery,
} from "@material-ui/core";
import { createTheme } from "./common/theme/colors";
import ColorProvider from "./common/theme/ColorProvider";
import Routes from "./Routes";

function App({}) {
  const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
  const theme = useMemo(() => createTheme(prefersDarkMode), [prefersDarkMode]);

  return (
    <>
      <ThemeProvider theme={theme}>
        <StylesProvider injectFirst>
          <ColorProvider theme={theme}>
            <Routes />
          </ColorProvider>
        </StylesProvider>
      </ThemeProvider>
    </>
  );
}

export default App;
  • styled components에서 자동완성을 위한 테마의 타입 생성
// types.ts
import { Theme } from "@material-ui/core/styles";

export interface IStyledTheme {
  theme: Theme;
}
  • styled components에서 material ui의 theme 기능 사용 가능
// 사용
import React from "react";
import styled from "styled-components";
import { IStyledTheme } from "../util/types";

// theme. 입력하면 머터리얼UI에 있는 속성들이 자동완성됨
const Content = styled.div`
  width: 400;
  ${({ theme }: IStyledTheme) => `padding: ${theme.spacing(2, 4, 3)};`}
  ${({ theme }: IStyledTheme) => `box-shadow: ${theme.shadows[5]};`}
  ${({ theme }: IStyledTheme) =>
    `background-color: ${theme.palette.background.paper};`}
`;