테마

테마 (Theming)

나만의 테마로 Material UI를 커스터마이즈하는 법을 알려드릴게요. 색상, 타이포그래피 등 훨씬 더 많은 것들을 바꿀 수 있어요.

테마는 컴포넌트의 색상, 표면의 어두움 정도, 그림자의 수준, 잉크(ink) 요소의 적절한 불투명도 등을 지정해 줘요.

테마는 앱에 일관된 톤을 적용할 수 있게 해 줘요. 프로젝트의 모든 디자인 측면을 커스터마이즈해서 비즈니스나 브랜드의 특정 요구를 충족시킬 수 있죠.

앱 간의 더 큰 일관성을 위해 라이트(light)와 다크(dark) 테마 타입 중에서 선택할 수 있어요. 기본적으로 컴포넌트는 라이트 테마 타입을 사용해요.

출처: 문서

:::success Material UI 테마 에이전트 스킬을 사용하면 AI 코딩 어시스턴트에게 createTheme, palette, color schemes, CSS variables, TypeScript augmentation에 대한 전체 컨텍스트를 제공할 수 있어요. :::

테마 제공자 (Theme provider)

Material UI 컴포넌트는 기본적으로 라이브러리의 기본 테마를 따릅니다. ThemeProvider를 사용해 애플리케이션에 커스텀 테마를 주입할 수 있어요.

ThemeProvider는 React의 context 기능을 이용해 테마를 컴포넌트 아래로 전달해요. 그래서 커스터마이즈하려는 컴포넌트들의 부모로 ThemeProvider가 있어야 해요. 자세한 내용은 API 섹션에서 확인할 수 있어요.

테마 설정 변수 (Theme configuration variables)

테마 설정 변수를 바꾸는 것이 Material UI를 내 요구에 맞추는 가장 효과적인 방법이에요. 다음 섹션들에서 가장 중요한 테마 변수들을 다룰게요:

기본 테마 전체를 보고 싶다면 기본 테마 섹션을 확인해 볼 수 있어요.

커스텀 변수 (Custom variables)

Material UI의 테마를 MUI System이나 다른 스타일링 솔루션과 함께 사용할 때, 테마에 추가 변수를 만들어 어디서든 사용할 수 있으면 편리해요. 예를 들면:

const theme = createTheme({
  status: {
    danger: orange[500],
  },
});

:::warning vars는 CSS 테마 변수를 위한 자동 생성 필드예요. 여기에 값을 전달하려고 하면 에러가 발생해요:

createTheme({
  vars: { ... }, // ❌ error
})

:::

TypeScript

Theme와 ThemeOptions에 새 변수를 추가하려면 module augmentation을 사용해야 해요.

declare module '@mui/material/styles' {
  interface Theme {
    status: {
      danger: string;
    };
  }
  // allow configuration using `createTheme()`
  interface ThemeOptions {
    status?: {
      danger?: string;
    };
  }
}
import Checkbox from '@mui/material/Checkbox';
import { createTheme, ThemeProvider, styled } from '@mui/material/styles';
import { orange } from '@mui/material/colors';

declare module '@mui/material/styles' {
  interface Theme {
    status: {
      danger: string;
    };
  }
  // allow configuration using `createTheme()`
  interface ThemeOptions {
    status?: {
      danger?: string;
    };
  }
}

const CustomCheckbox = styled(Checkbox)(({ theme }) => ({
  color: theme.status.danger,
  '&.Mui-checked': {
    color: theme.status.danger,
  },
}));

const theme = createTheme({
  status: {
    danger: orange[500],
  },
});

export default function CustomStyles() {
  return (
    <ThemeProvider theme={theme}>
      <CustomCheckbox defaultChecked />
    </ThemeProvider>
  );
}

theme.palette에 추가 변수를 넣으려면 palette 커스터마이즈를 참고해 주세요.

테마 빌더 (Theme builder)

커뮤니티에서 테마를 빌드할 수 있는 훌륭한 도구들을 만들었어요:

  • mui-theme-creator: Material UI 컴포넌트 라이브러리를 위한 테마를 설계·커스터마이즈하는 도구예요. 다양한 컴포넌트가 테마의 영향을 어떻게 받는지 보여주는 기본 사이트 템플릿을 포함해요.
  • MUI Theme Builder: Material UI 테마를 생성·미리보기·편집할 수 있는 도구예요.
  • Material palette generator: Material palette 생성기는 입력한 어떤 색상이든 palette를 생성하는 데 사용할 수 있어요.

컴포넌트에서 테마 접근하기 (Accessing the theme in a component)

함수형 React 컴포넌트 안에서는 useTheme 훅을 사용해 테마 변수에 접근할 수 있어요:

import { useTheme } from '@mui/material/styles';

function DeepChild() {
  const theme = useTheme();
  return <span>{`spacing ${theme.spacing}`}</span>;
}

테마 중첩하기 (Nesting the theme)

여러 개의 테마 제공자를 중첩할 수 있어요.

import { createTheme, ThemeProvider } from '@mui/material/styles';
import Checkbox from '@mui/material/Checkbox';
import { green, orange } from '@mui/material/colors';

const outerTheme = createTheme({
  palette: {
    primary: {
      main: orange[500],
    },
  },
});

const innerTheme = createTheme({
  palette: {
    primary: {
      main: green[500],
    },
  },
});

export default function ThemeNesting() {
  return (
    <ThemeProvider theme={outerTheme}>
      <Checkbox defaultChecked />
      <ThemeProvider theme={innerTheme}>
        <Checkbox defaultChecked />
      </ThemeProvider>
    </ThemeProvider>
  );
}

안쪽 테마는 바깥쪽 테마를 덮어씁니다(override). 함수를 제공하면 바깥쪽 테마를 확장할 수도 있어요:

import { createTheme, Theme, ThemeProvider } from '@mui/material/styles';
import Checkbox from '@mui/material/Checkbox';
import { green, orange } from '@mui/material/colors';

const outerTheme = createTheme({
  palette: {
    secondary: {
      main: orange[500],
    },
  },
});

export default function ThemeNestingExtend() {
  return (
    <ThemeProvider theme={outerTheme}>
      <Checkbox defaultChecked color="secondary" />
      <ThemeProvider
        theme={(theme: Theme) =>
          createTheme({
            ...theme,
            palette: {
              ...theme.palette,
              primary: {
                main: green[500],
              },
            },
          })
        }
      >
        <Checkbox defaultChecked />
        <Checkbox defaultChecked color="secondary" />
      </ThemeProvider>
    </ThemeProvider>
  );
}

CSS 테마 변수 (CSS theme variables)

테마로부터 CSS 변수를 생성하려면 테마 설정에 cssVariables를 true로 설정한 뒤 ThemeProvider에 전달하면 돼요:

const theme = createTheme({
  cssVariables: true,
});

function App() {
  return <ThemeProvider theme={theme}>...</ThemeProvider>;
}

이렇게 하면 CSS 테마 변수를 담은 전역 스타일시트가 생성돼요:

:root {
  --mui-palette-primary-main: #1976d2;
  /* ...other variables */
}

ThemeProvider 아래의 모든 컴포넌트는 원시 값 대신 이러한 CSS 테마 변수를 사용해요.

- color: #1976d2;
+ color: var(--mui-palette-primary-main);

이 기능에 대해 더 자세히 알고 싶다면 CSS 테마 변수 가이드를 참고해 주세요.

API

createTheme(options, ...args) => theme

받은 옵션을 기반으로 테마를 생성해요. 그런 다음 이를 ThemeProvider에 prop으로 전달해요.

인자 (Arguments)

  1. options (object): 불완전한 테마 객체를 받아 빠진 부분을 채워요.
  2. ...args (object[]): 곧 반환될 테마와 인자를 깊게 병합(deep merge)해요.

:::warning createTheme() 함수가 처리하는 것은 첫 번째 인자(options)뿐이에요. 여러 인자를 전달하는 것은 현재 하위 호환성을 위해 동작하지만, 이 동작은 향후 버전에서 제거될 수 있어요. 코드가 앞으로도 호환되게 하려면 테마 객체를 직접 깊게 병합해 그 결과를 createTheme()에 단일 객체로 전달해야 해요. :::

import { deepmerge } from '@mui/utils';
import { createTheme } from '@mui/material/styles';

const theme = createTheme(deepmerge(options1, options2));

반환 값 (Returns)

theme (object): 완전하고 바로 사용할 수 있는 테마 객체.

예시 (Examples)

import { createTheme } from '@mui/material/styles';
import { green, purple } from '@mui/material/colors';

const theme = createTheme({
  palette: {
    primary: {
      main: purple[500],
    },
    secondary: {
      main: green[500],
    },
  },
});

테마 구성: 테마 옵션이 다른 옵션을 정의하는 데 사용 (Theme composition: using theme options to define other options)

어떤 테마 옵션의 값이 다른 테마 옵션에 의존할 때는 테마를 단계적으로 구성해야 해요.

import { createTheme } from '@mui/material/styles';

let theme = createTheme({
  palette: {
    primary: {
      main: '#0052cc',
    },
    secondary: {
      main: '#edf2ff',
    },
  },
});

theme = createTheme(theme, {
  palette: {
    info: {
      main: theme.palette.secondary.main,
    },
  },
});

테마를 만드는 것은 두 단계의 구성 과정으로 생각하면 돼요: 먼저 기본 디자인 옵션을 정의하고, 그 다음 이 디자인 옵션을 사용해 다른 옵션을 구성하는 거예요.

경고(WARNING): theme.vars는 CSS 변수 지원을 위한 비공개(private) 필드예요. 커스텀 객체에는 다른 이름을 사용해 주세요.

defaultProps에서 className와 style props 병합하기 (Merging className and style props in defaultProps)

기본적으로 컴포넌트가 테마에 정의된 defaultProps를 가질 때, 컴포넌트에 전달된 props는 기본 props를 완전히 덮어씁니다.

import { createTheme } from '@mui/material/styles';

const theme = createTheme({
  components: {
    MuiButton: {
      defaultProps: {
        className: 'default-button-class',
        style: { marginTop: 8 },
      },
    },
  },
});

// className will be: "custom-button-class" (default ignored)
// style will be: { color: 'blue' } (default ignored)
<Button className="custom-button-class" style={{ color: 'blue' }}>
  Click me
</Button>;

이 동작을 바꿔서 className과 style props를 대체(replace)하는 대신 병합(merge)하도록 테마를 설정할 수 있어요.

이렇게 하려면 theme.components.mergeClassNameAndStyle를 true로 설정하세요:

import { createTheme } from '@mui/material/styles';

const theme = createTheme({
  components: {
    mergeClassNameAndStyle: true,
    MuiButton: {
      defaultProps: {
        className: 'default-button-class',
        style: { marginTop: 8 },
      },
    },
  },
});

이 설정을 적용했을 때 위 예시는 이렇게 동작해요:

// className will be: "default-button-class custom-button-class"
// style will be: { marginTop: 8, color: 'blue' }
<Button className="custom-button-class" style={{ color: 'blue' }}>
  Click me
</Button>

responsiveFontSizes(theme, options) => theme

받은 옵션을 기반으로 반응형 타이포그래피 설정을 생성해요.

인자 (Arguments)

  1. theme (object): 향상시킬 테마 객체.
  2. options (object [optional]):
  • breakpoints (array<string> [optional]): 기본값은 ['sm', 'md', 'lg']. breakpoints(식별자) 배열.
  • disableAlign (bool [optional]): 기본값은 false. 글꼴 크기가 살짝 조정돼 줄 높이가 유지되고 Material Design의 4px 줄 높이 그리드에 맞춰지는지 여부. 이 기능은 테마 스타일에 단위가 없는(unitless) line height가 필요해요.
  • factor (number [optional]): 기본값은 2. 이 값은 글꼴 크기 조정의 강도를 결정해요. 값이 클수록 작은 화면에서 글꼴 크기 간 차이가 작아져요. 값이 작을수록 작은 화면에서 글꼴 크기가 커져요. 값은 1보다 커야 해요.
  • variants (array<string> [optional]): 기본값은 모두(all). 처리할 타이포그래피 variants.

반환 값 (Returns)

theme (object): 반응형 타이포그래피가 적용된 새 테마.

예시 (Examples)

import { createTheme, responsiveFontSizes } from '@mui/material/styles';

let theme = createTheme();
theme = responsiveFontSizes(theme);

enhanceHighContrast(theme, tokens) => theme

@media (forced-colors: active) 오버라이드를 테마에 적용해서 Windows 고대비 / Forced Colors 모드에서 컴포넌트의 가시성을 개선해요. v9.1.0에서 사용할 수 있어요. 완전히 생성된 테마를 받아 향상된 버전을 반환해요.

인자 (Arguments)

  1. theme (object): 향상시킬 테마 객체.
  2. tokens (object [optional]): 개별 기본값을 덮어쓸 CSS 시스템 색상 키워드 객체.
Token Default Description
disabled GrayText Color for disabled elements
error ActiveText Color for error states
selectedBackground SelectedItem Background color for selected items
selectedText SelectedItemText Text color on selected items
activeBackground Highlight Background color for active/toggled controls
activeText HighlightText Text color on active/toggled controls
buttonBorder ButtonBorder Border color for interactive controls
buttonText ButtonText Text/icon color on buttons
canvas Canvas Background color for the page/canvas

반환 값 (Returns)

theme (object): forced-colors 오버라이드가 영향을 받는 컴포넌트에 적용된 새 테마.

예시 (Examples)

import { createTheme, enhanceHighContrast } from '@mui/material/styles';

// Use defaults
let theme = createTheme();
theme = enhanceHighContrast(theme);
// Override individual tokens
let theme = createTheme();
theme = enhanceHighContrast(theme, {
  activeBackground: 'SelectedItem',
  activeText: 'SelectedItemText',
});

unstable_createMuiStrictModeTheme(options, ...args) => theme

경고(WARNING): 이 메서드는 프로덕션에서 사용하지 마세요.

React.StrictMode 안에서 Warning: findDOMNode is deprecated in StrictMode 같은 경고의 수를 줄여주는 테마를 생성해요.

요구 사항 (Requirements)

현재 unstable_createMuiStrictModeTheme는 추가 요구 사항이 없어요.

인자 (Arguments)

  1. options (object): 불완전한 테마 객체를 받아 빠진 부분을 채워요.
  2. ...args (object[]): 곧 반환될 테마와 인자를 깊게 병합해요.

반환 값 (Returns)

theme (object): 완전하고 바로 사용할 수 있는 테마 객체.

예시 (Examples)

import { unstable_createMuiStrictModeTheme } from '@mui/material/styles';

const theme = unstable_createMuiStrictModeTheme();

function App() {
  return (
    <React.StrictMode>
      <ThemeProvider theme={theme}>
        <LandingPage />
      </ThemeProvider>
    </React.StrictMode>
  );
}

ThemeProvider

이 컴포넌트는 theme prop을 받아서 감싸고 있는 전체 React 트리에 적용해요. 가급적 컴포넌트 트리의 루트에서 사용하는 것이 좋아요.

Props

Name Type Description
children * node Your component tree.
theme * union: object | func A theme object, usually the result of createTheme(). The provided theme will be merged with the default theme. You can provide a function to extend the outer theme.

예시 (Examples)

import * as React from 'react';
import { red } from '@mui/material/colors';
import { ThemeProvider, createTheme } from '@mui/material/styles';

const theme = createTheme({
  palette: {
    primary: {
      main: red[500],
    },
  },
});

function App() {
  return <ThemeProvider theme={theme}>...</ThemeProvider>;
}

더 알아보기 (Learn more)