Styles API

Styles API

Styles API란 무엇인가요

Styles API는 Mantine 컴포넌트 안의 어떤 요소든 인라인 또는 theme 객체로 스타일을 커스터마이즈할 수 있게 해주는 props와 기법의 집합이에요. 스타일이 있는 모든 Mantine 컴포넌트는 Styles API를 지원해요.

출처: 문서

본문

Styles API selectors

Styles API를 지원하는 모든 Mantine 컴포넌트는 컴포넌트 내부의 요소에 스타일을 적용하는 데 사용할 수 있는 요소 이름 집합을 가지고 있어요. 간단히 하기 위해 Mantine 문서에서는 이 요소 이름들을 selector라고 불러요. selector 정보는 컴포넌트 문서의 Styles API 탭에서 찾을 수 있어요.

Button 컴포넌트 selector 예시:

Selector 정적 selector 설명
root .mantine-Button-root 루트 요소
loader .mantine-Button-loader Loader 컴포넌트, loading prop이 설정될 때만 표시돼요
inner .mantine-Button-inner 다른 모든 요소를 포함하며 root 요소의 자식이에요
section .mantine-Button-section 버튼의 왼쪽/오른쪽 section
label .mantine-Button-label 버튼 children

컴포넌트 props와 theme.components 양쪽에서 classNames와 styles에 이 selector들을 사용할 수 있어요:

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

function ClassNamesDemo() {
  return <Button classNames={{ root: 'my-root-class' }}>Button</Button>;
}

function StylesDemo() {
  return <Button styles={{ root: { backgroundColor: 'red' } }}>Button</Button>;
}

const theme = createTheme({
  components: {
    Button: Button.extend({
      classNames: {
        root: 'my-root-class',
        label: 'my-label-class',
        inner: 'my-inner-class',
      },
      styles: {
        root: { backgroundColor: 'red' },
        label: { color: 'blue' },
        inner: { fontSize: 20 },
      },
    }),
  },
});

function ProviderDemo() {
  return (
    <MantineProvider theme={theme}>
      <Button>Button</Button>
    </MantineProvider>
  );
}

classNames prop

classNames prop으로 Mantine 컴포넌트의 내부 요소에 클래스를 추가할 수 있어요. 요소 이름을 키로, 클래스를 값으로 가지는 객체를 받아요:

import { useState } from 'react';
import { TextInput } from '@mantine/core';
import classes from './Demo.module.css';

function Demo() {
  const [value, setValue] = useState('');
  const [focused, setFocused] = useState(false);
  const floating = focused || value.length > 0 || undefined;

  return (
    <TextInput
      label="Floating label input"
      classNames={{ label: classes.label, input: classes.input }}
      onFocus={() => setFocused(true)}
      onBlur={() => setFocused(false)}
      value={value}
      onChange={(event) => setValue(event.currentTarget.value)}
    />
  );
}

theme.components의 classNames

특정 유형의 모든 컴포넌트에 적용하려면 theme.components에서도 classNames를 정의할 수 있어요:

import { useState } from 'react';
import {
  createTheme,
  MantineProvider,
  TextInput,
} from '@mantine/core';
// Styles are the same as in previous example
import classes from './Demo.module.css';

const theme = createTheme({
  components: {
    TextInput: TextInput.extend({
      classNames: {
        root: classes.root,
        input: classes.input,
        label: classes.label,
      },
    }),
  },
});

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

컴포넌트 CSS variables

대부분의 Mantine 컴포넌트는 색상, 크기, padding 및 다른 속성을 정의하기 위해 CSS variables를 사용해요. theme.components의 커스텀 CSS variables resolver 함수로 이 값들을 재정의하거나 vars prop으로 전달할 수 있어요.

CSS variables 정보는 컴포넌트 문서의 Styles API 탭에서 찾을 수 있어요. Button 컴포넌트 CSS variables 예시:

Selector 변수 설명
root --button-bg background를 제어해요
--button-bd border를 제어해요
--button-hover hover 시 background를 제어해요
--button-color 텍스트 color를 제어해요
--button-hover-color hover 시 텍스트 color를 제어해요
--button-radius border-radius를 제어해요
--button-height 버튼의 height를 제어해요
--button-padding-x 버튼의 가로 padding을 제어해요
--button-fz 버튼의 font-size를 제어해요
--button-justify inner 요소의 justify-content를 제어해요

Button 컴포넌트에 더 많은 크기를 추가하는 데 사용하는 커스텀 CSS variables resolver 함수 예시:

import { Button, Group, MantineProvider, createTheme } from '@mantine/core';

const theme = createTheme({
  components: {
    Button: Button.extend({
      vars: (theme, props) => {
        if (props.size === 'xxl') {
          return {
            root: {
              '--button-height': '60px',
              '--button-padding-x': '30px',
              '--button-fz': '24px',
            },
          };
        }

        if (props.size === 'xxs') {
          return {
            root: {
              '--button-height': '24px',
              '--button-padding-x': '10px',
              '--button-fz': '10px',
            },
          };
        }

        return { root: {} };
      },
    }),
  },
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <Group>
        <Button size="xxl">XXL Button</Button>
        <Button size="xxs">XXS Button</Button>
      </Group>
    </MantineProvider>
  );
}

styles prop

styles prop은 classNames와 같은 방식으로 동작하지만 인라인 스타일을 적용해요. 인라인 스타일은 클래스보다 특이성(specificity)이 높으므로 !important를 사용하지 않고는 클래스로 재정의할 수 없다는 점에 주의하세요. styles prop 안에서는 pseudo-classes(예: :hover, :first-of-type)와 미디어 쿼리를 사용할 수 없어요.

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

function Demo() {
  const gradient =
    'linear-gradient(45deg, var(--mantine-color-pink-filled) 0%, var(--mantine-color-orange-filled) 50%, var(--mantine-color-yellow-filled) 100%)';

  return (
    <Button
      styles={{
        root: {
          backgroundImage: gradient,
        },
      }}
    >
      Gradient button
    </Button>
  );
}

styles prop 사용

문서의 일부 예시와 데모는 편의를 위해 styles prop을 사용하지만, classNames prop이 더 유연하고 성능도 더 좋으므로 styles prop을 컴포넌트 스타일링의 주요 수단으로 쓰는 것은 권장되지 않아요.

컴포넌트 props에 기반한 Styles API

classNames와 styles에 콜백 함수를 전달할 수도 있어요. 이 함수는 첫 번째 인자로 theme, 두 번째로 컴포넌트 props를 받아요. 클래스 객체(classNames용) 또는 styles 객체(styles용)를 반환해야 해요.

이 기능을 사용하면 컴포넌트 props에 따라 스타일을 조건부로 적용할 수 있어요. 예를 들어 input이 required이면 TextInput label 색상을 바꾸거나, input이 틀리면 배경색을 바꿀 수 있어요:

import cx from 'clsx';
import { MantineProvider, createTheme, TextInput } from '@mantine/core';
import classes from './Demo.module.css';

const theme = createTheme({
  components: {
    TextInput: TextInput.extend({
      classNames: (_theme, props) => ({
        label: cx({ [classes.labelRequired]: props.required }),
        input: cx({ [classes.inputError]: props.error }),
      }),
    }),
  },
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <TextInput label="Required input" required />
      <TextInput label="Input with error" error />
    </MantineProvider>
  );
}

정적 클래스

Styles API를 지원하는 모든 컴포넌트에는 classNames나 styles props 없이 컴포넌트에 스타일을 적용할 수 있는 정적 클래스도 포함돼 있어요. 기본적으로 정적 클래스는 .mantine-{ComponentName}-{selector} 형식이에요. 예를 들어 Button 컴포넌트의 root selector는 .mantine-Button-root 클래스를 가져요.

정적 클래스를 사용해 CSS나 다른 어떤 스타일링 솔루션으로 컴포넌트에 스타일을 적용할 수 있어요:

.mantine-Button-root {
  background-color: red;
}

정적 클래스의 접두사는 MantineProvider의 classNamesPrefix로 바꿀 수 있어요.

컴포넌트 클래스

각 컴포넌트의 클래스는 Component.classes 객체에서 사용할 수 있어요. 예를 들어 Button의 클래스는 Button.classes에서 찾을 수 있어요:

Key Class
root m_77c9d27d
inner m_80f1301b
label m_811560b9
section m_a74036a
loader m_a25b86ee
group m_80d6d844
groupSection m_70be2a01

이 클래스들을 사용해 Mantine 컴포넌트와 동일한 스타일을 가진 컴포넌트를 만들 수 있어요:

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

function Demo() {
  return <div className={Button.classes.root}>Custom button</div>;
}

Attributes

attributes prop을 사용해 Mantine 컴포넌트의 내부 요소에 속성을 전달할 수 있어요. 예를 들어 테스트 목적으로 data 속성을 추가하는 데 사용할 수 있어요:

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

function Demo() {
  return (
    <Button attributes={{ root: { 'data-test': 'button' } }}>
      Button
    </Button>
  );
}

더 알아보기 (Learn more)