색상 체계

색상 체계 (Color schemes)

MantineProvider는 애플리케이션에서 색상 체계(color scheme) 컨텍스트를 관리해요. defaultColorScheme prop으로 기본 색상 체계 값을 구성할 수 있어요; 가능한 값은 light, dark, auto(시스템 색상 체계 사용)예요. 기본값은 light예요.

import { MantineProvider } from '@mantine/core';

function Demo() {
  return (
    <MantineProvider defaultColorScheme="light">
      {/* Your app here */}
    </MantineProvider>
  );
}

출처: 문서

본문

data-mantine-color-scheme 속성

MantineProvider가 마운트되면 사용자가 이전에 선택한 값이나 defaultColorScheme prop의 값으로 `` 요소에 data-mantine-color-scheme 속성을 설정해요. data-mantine-color-scheme 속성은 모든 컴포넌트 스타일에서 각 컴포넌트가 어떤 색상을 사용할지 결정하는 데 사용돼요.

use-mantine-color-scheme 훅

useMantineColorScheme 훅은 현재 색상 체계 값을 가져오고 설정하는 데 사용할 수 있어요:

function useMantineColorScheme(): {
  /** Current color scheme value */
  colorScheme: 'dark' | 'light' | 'auto';

  /** Sets colors scheme to given value */
  setColorScheme: (colorScheme: 'dark' | 'light' | 'auto') => void;

  /** Toggles color scheme to the opposite value; if value is 'auto', color scheme is inferred from the OS settings */
  toggleColorScheme: () => void;

  /** Clears the color scheme value from storage and sets it to `defaultColorScheme` */
  clearColorScheme: () => void;
};
import { useMantineColorScheme, Button, Group } from '@mantine/core';

function Demo() {
  const { setColorScheme, clearColorScheme } = useMantineColorScheme();

  return (
    <Group>
      <Button onClick={() => setColorScheme('light')}>Light</Button>
      <Button onClick={() => setColorScheme('dark')}>Dark</Button>
      <Button onClick={() => setColorScheme('auto')}>Auto</Button>
      <Button onClick={clearColorScheme}>Clear</Button>
    </Group>
  );
}

use-computed-color-scheme 훅

useComputedColorScheme은 계산된 색상 체계 값(light 또는 dark)을 반환해요. 색상 체계 토글 로직을 구현하는 데 사용할 수 있어요:

import {
  useComputedColorScheme,
  useMantineColorScheme,
} from '@mantine/core';

function Demo() {
  // -> colorScheme is 'auto' | 'light' | 'dark'
  const { colorScheme, setColorScheme } = useMantineColorScheme();

  // -> computedColorScheme is 'light' | 'dark', argument is the default value
  const computedColorScheme = useComputedColorScheme('light');

  // Incorrect color scheme toggle implementation
  // If colorScheme is 'auto', then it is not possible to
  // change color scheme correctly in all cases:
  // 'auto' can mean both light and dark
  const toggleColorScheme = () => {
    setColorScheme(colorScheme === 'dark' ? 'light' : 'dark');
  };

  // Correct color scheme toggle implementation
  // computedColorScheme is always either 'light' or 'dark'
  const toggleColorScheme = () => {
    setColorScheme(computedColorScheme === 'dark' ? 'light' : 'dark');
  };
}

색상 체계 변경 중 전환(transition)

기본적으로 색상 체계가 바뀔 때 모든 요소의 전환이 비활성화되어 일관되지 않은 애니메이션을 방지해요. 색상 체계 변경 중 전환을 활성화하려면 useMantineColorScheme 훅에서 keepTransitions: true 옵션을 설정해요:

import { useMantineColorScheme } from '@mantine/core';

function Demo() {
  const { colorScheme, setColorScheme } = useMantineColorScheme({
    keepTransitions: true,
  });
}

색상 체계 값 주의사항

기본적으로 색상 체계 값은 local storage에 저장되며, 부정확한 색상 체계의 깜빡임(flash)을 피하기 위해 컴포넌트가 마운트되기 전에 값이 state에 저장돼요. 즉 색상 체계 값은 클라이언트와 서버에서 다를 수 있는데, 서버는 local storage에 접근할 수 없고 항상 기본값을 사용하기 때문이에요.

애플리케이션에 서버 사이드 렌더링이 있다면(예: Next.js나 React Router 사용) hydration 문제를 피하기 위해 애플리케이션에서 colorScheme 값을 사용할 수 없어요. 대신 postcss-preset-mantine의 dark와 light 믹스인을 사용해 색상 체계 값에 따라 요소를 숨기는 스타일을 생성할 수 있어요:

import { ActionIcon, useMantineColorScheme, useComputedColorScheme } from '@mantine/core';
import { SunIcon, MoonIcon } from '@phosphor-icons/react';
import cx from 'clsx';
import classes from './Demo.module.css';

function Demo() {
  const { setColorScheme } = useMantineColorScheme();
  const computedColorScheme = useComputedColorScheme('light', { getInitialValueInEffect: true });

  return (
    <ActionIcon
      onClick={() => setColorScheme(computedColorScheme === 'light' ? 'dark' : 'light')}
      variant="default"
      size="xl"
      aria-label="Toggle color scheme"
    >
      {computedColorScheme === 'light' ? <MoonIcon /> : <SunIcon />}
    </ActionIcon>
  );
}

클라이언트 전용 애플리케이션의 colorScheme

colorScheme 값을 클라이언트 전용 애플리케이션(예: Vite나 create-react-app)에서는 안전하게 사용할 수 있어요. 이 경우 hydration이 없으므로 hydration 오류도 발생할 수 없어요.

ColorSchemeScript

ColorSchemeScript 컴포넌트는 hydration 전에 `` 요소의 data-mantine-color-scheme 속성을 사용자가 선택한 값이나 defaultColorScheme prop 값으로 설정하는 script 태그를 렌더링해요. 서버 사이드 렌더링 애플리케이션, 예를 들어 Next.js나 React Router에서 부정확한 색상 체계의 깜빡임을 피하는 데 사용돼요. ColorSchemeScript 컴포넌트를 어디에 렌더링할지는 프레임워크별 가이드를 따라 알아보세요.

ColorSchemeScript 컴포넌트가 생성한 `` 태그에 nonce 속성 같은 추가 props를 더할 수 있어요:

import { ColorSchemeScript } from '@mantine/core';

function Demo() {
  return (
    <ColorSchemeScript nonce="8IBTHwOdqNKA" />
  );
}

자동 색상 체계

시스템 색상 체계를 사용하려면 MantineProvider와 ColorSchemeScript에 defaultColorScheme="auto"를 설정해요. 이 경우 색상 체계 값은 사용자의 OS가 제어해요:

import { ColorSchemeScript, MantineProvider } from '@mantine/core';

function Demo() {
  return (
    <>
      <ColorSchemeScript defaultColorScheme="auto" />
      <MantineProvider defaultColorScheme="auto">
        {/* Your app here */}
      </MantineProvider>
    </>
  );
}

색상 체계 매니저

기본적으로 색상 체계 값은 local storage에 저장되지만, 값을 다른 외부 저장소에 저장하는 나만의 색상 체계 매니저를 구현할 수 있어요.

색상 체계 매니저는 다음 메서드를 가져야 해요:

interface MantineColorSchemeManager {
  /** Function to retrieve color scheme value from external storage, for example window.localStorage */
  get: (defaultValue: MantineColorScheme) => MantineColorScheme;

  /** Function to set color scheme value in external storage, for example window.localStorage */
  set: (value: MantineColorScheme) => void;

  /** Function to subscribe to color scheme changes triggered by external events */
  subscribe: (
    onUpdate: (colorScheme: MantineColorScheme) => void
  ) => void;

  /** Function to unsubscribe from color scheme changes triggered by external events */
  unsubscribe: () => void;

  /** Function to clear value from external storage */
  clear: () => void;
}

보통 색상 체계 매니저를 구성할 방법을 제공하기 위해 creator 함수로 감싸는 것이 좋아요. 기본 local storage 기반 색상 체계 매니저 예시:

import {
  isMantineColorScheme,
  MantineColorScheme,
  MantineColorSchemeManager,
} from '@mantine/core';

export interface LocalStorageColorSchemeManagerOptions {
  /** Local storage key used to retrieve value with `localStorage.getItem(key)`, `mantine-color-scheme` by default */
  key?: string;
}

export function localStorageColorSchemeManager({
  key = 'mantine-color-scheme',
}: LocalStorageColorSchemeManagerOptions = {}): MantineColorSchemeManager {
  let handleStorageEvent: (event: StorageEvent) => void;

  return {
    get: (defaultValue) => {
      if (typeof window === 'undefined') {
        return defaultValue;
      }

      try {
        return (
          (window.localStorage.getItem(key) as MantineColorScheme) ||
          defaultValue
        );
      } catch {
        return defaultValue;
      }
    },

    set: (value) => {
      try {
        window.localStorage.setItem(key, value);
      } catch (error) {
        // eslint-disable-next-line no-console
        console.warn(
          '[@mantine/core] Local storage color scheme manager was unable to save color scheme.',
          error
        );
      }
    },

    subscribe: (onUpdate) => {
      handleStorageEvent = (event) => {
        if (
          event.storageArea === window.localStorage &&
          event.key === key
        ) {
          isMantineColorScheme(event.newValue) &&
            onUpdate(event.newValue);
        }
      };

      window.addEventListener('storage', handleStorageEvent);
    },

    unsubscribe: () => {
      window.removeEventListener('storage', handleStorageEvent);
    },

    clear: () => {
      window.localStorage.removeItem(key);
    },
  };
}

그런 다음 커스텀 색상 체계 매니저를 MantineProvider에 전달할 수 있어요:

import { MantineProvider } from '@mantine/core';
import { localStorageColorSchemeManager } from './localStorageColorSchemeManager';

const colorSchemeManager = localStorageColorSchemeManager({
  key: 'my-color-scheme',
});

function Demo() {
  return (
    <MantineProvider colorSchemeManager={colorSchemeManager}>
      {/* Your app here */}
    </MantineProvider>
  );
}

기본 색상 체계

기본 색상 체계 값은 사용자가 아직 어떤 색상 체계도 선택하지 않았을 때 사용돼요. MantineProvider와 ColorSchemeScript 양쪽에 설정해야 해요. defaultColorScheme이 설정되지 않으면 light가 사용돼요.

import { ColorSchemeScript, MantineProvider } from '@mantine/core';

function Demo() {
  return (
    <>
      <ColorSchemeScript defaultColorScheme="dark" />
      <MantineProvider defaultColorScheme="dark">
        {/* Your app here */}
      </MantineProvider>
    </>
  );
}

색상 체계 강제

forceColorScheme prop으로 색상 체계 값을 light 또는 dark로 강제할 수 있어요. MantineProvider와 ColorSchemeScript 양쪽에 설정해야 해요. forceColorScheme이 설정되면 defaultColorScheme과 colorSchemeManager는 무시돼요. forceColorScheme이 설정되면 setColorScheme 함수로 색상 체계 값을 바꿀 수 없어요.

import { ColorSchemeScript, MantineProvider } from '@mantine/core';

function Demo() {
  return (
    <>
      <ColorSchemeScript forceColorScheme="dark" />
      <MantineProvider forceColorScheme="dark">
        {/* Your app here */}
      </MantineProvider>
    </>
  );
}

lightHidden, darkHidden props

모든 Mantine 컴포넌트는 특정 색상 체계에서 컴포넌트를 숨기는 데 사용할 수 있는 lightHidden과 darkHidden props를 지원해요:

import { Button } from '@mantine/core';

function Demo() {
  return (
    <>
      <Button darkHidden>Visible in dark color scheme only</Button>
      <Button lightHidden>Visible in light color scheme only</Button>
    </>
  );
}

JavaScript 비활성 상태

JavaScript가 비활성화된 사용자를 지원해야 한다면 `` 요소에 data-mantine-color-scheme 속성을 수동으로 설정해야 해요.

JavaScript 비활성 상태를 지원하는 Next.js app router 예시:

import '@mantine/core/styles.css';

import { ColorSchemeScript, MantineProvider } from '@mantine/core';

export const metadata = {
  title: 'My Mantine app',
  description: 'I have followed setup instructions carefully',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" data-mantine-color-scheme="light">
      <head>
        <ColorSchemeScript />
      </head>
      <body>
        <MantineProvider>{children}</MantineProvider>
      </body>
    </html>
  );
}

더 알아보기 (Learn more)