🐶

Mantine v7.0.0のまとめ

2023/10/02に公開

こちらは以下の記事の一部(既存コンポーネントの変更点や新規コンポーネントなどの一部が除外してあります)を翻訳して軽くまとめた記事になります。
https://v7.mantine.dev/changelog/7-0-0

Migration to native CSS

Mantine はスタイル生成を Emotion に依存しなくなりました。これによりパフォーマンスの向上とバンドルサイズが小さくなりました。またCSS-in-JSがサポートされていないNext.jsのappディレクトリなどで使用できるようになりました。
重要な変更点は以下の3点

  • createStyles関数は使用できなくなりました。代わりにCSS modulesや他のスタイリングライブラリを使用してください。
  • sx propsのサポート終了。代わりにclassNameやstyle propsを使用してください。
  • styles propsはネストされたセレクタをサポートしなくなりました。

Mantineのスタイル設定にはCSS modulesが推奨されるようになりました。CSS modulesを使用したスタイリングに更新する場合は以下の記事に従ってください。
https://v7.mantine.dev/guides/6x-to-7x

Vanilla extract integration

CSS-in-jsの構文を使いたいならVanilla extractが使用できる。詳しくは以下の記事へ。
https://v7.mantine.dev/styles/vanilla-extract

System color scheme support

全てのコンポーネントがsystem color scheme(システムカラーテーマ)をサポートするようになりました。colorSchemeの値がautoの場合、コンポーネントはprefers-color-schemeメディアクエリを使用して、ユーザーが明るい配色を好むか暗い配色を好むかを判断します。

autoはデフォルト値ではなく、以下のようにMantineProvierとColorSchemeScriptの両方で設定が必要です。

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

function Demo() {
  return (
    <>
      <ColorSchemeScript defaultColorScheme="auto" />
      <MantineProvider defaultColorScheme="auto">
        <App />
      </MantineProvider>
    </>
  );
}

Built-in color scheme manager

MantineProvierにビルトインのカラーテーママネージャーが搭載されました。useMantineColorSchemeフックを使用してカラーテーマを変更することができます。値にはlight,dark,autoがセットできます。

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>
  );
}

CSS modules and PostCSS preset

postcss-preset-mantineはスタイリングの際に便利な関数とmixinを提供してくれます。こちらの使用を推奨しています。

Global styles

Mantineはもはやnormalize.cssを含んでいません。その代わりに、最低限のグローバルスタイルを使用します。これらのスタイルは @mantine/core パッケージの一部であり、アプリケーションで @mantine/core/styles.css をインポートすると自動的に適用されます。この最低限のグローバルスタイルを削除することはできません。

Mantine as a headless UI library

アプリケーションの中で @mantine/*/styles.css をインポートしなければ、Mantine をヘッドレスライブラリとして使うことができます。

createTheme function

createTheme関数でテーマの上書きができます。その際にプロパティ名を自動補完してくれます。

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

const theme = createTheme({
  fontFamily: 'sans-serif',
  primaryColor: 'orange',
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <App />
    </MantineProvider>
  );
}

Components extend functions

default propsまたはStyles APIをサポートするすべてのコンポーネントにextend関数が追加され、テーマ上でコンポーネントのdefaultProps、classNames、stylesをカスタマイズする際に自動補完をしてくれるようになりました。

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

const theme = createTheme({
  components: {
    TextInput: TextInput.extends({
      styles: (theme, props) => ({
        input: {
          fontSize: props.size === 'compact' ? theme.fontSizes.sm : undefined,
        }
      })
      classNames: {
        root: classes.root,
        input: classes.input,
        label: classes.label,
      },

      defaultProps: {
        size: 'compact',
      },
    }),
  },
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <App />
    </MantineProvider>
  );
}

classNames based on component props

classNamesとstylesでコンポーネントのpropsを取得し、条件付きでスタイルを適用できるようになりました。

Demo.module.css
.labelRequired {
  color: var(--mantine-color-red-filled);
}

.inputError {
  background-color: var(--mantine-color-red-light);
}
Demo.tsx
import cx from 'clsx';
import { TextInput } from '@mantine/core';
import { useEffect, useState } from 'react';
import { theme } from '../theme';
import classes from './Demo.module.css';

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <TextInput
        label="Required input"
        placeholder="Required input"
        required
        classNames={(_, props) => ({
          label: cx({ [classes.labelRequired]: props.required }),
          input: cx({ [classes.inputError]: props.error }),
        })}
      />
    </MantineProvider>
  );
}

Components CSS variables

ほとんどの Mantine コンポーネントは、色、サイズ、パディング、その他のプロパティを定義するために CSS変数を使用します。CSS変数の情報は、コンポーネントドキュメントの「Styles API」タブで確認できます。これらの値を上書きするには、theme.componentsのカスタムCSS変数リゾルバー関数を使うか、vars propを使用します。

  • theme.componentsをカスタムする場合
import { Button, rem, Group, MantineProvider, createTheme } from '@mantine/core';

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

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

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

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <Group>
        <Button size="xxl">XXL Button</Button>
        <Button size="xxs">XXS Button</Button>
      </Group>
    </MantineProvider>
  );
}
  • vars propを使用する場合
import { Button, rem, PartialVarsResolver, ButtonFactory, Group } from '@mantine/core';

const varsResolver: PartialVarsResolver<ButtonFactory> = (theme, props) => {
  if (props.size === 'xxl') {
    return {
      root: {
        '--button-height': rem(60),
        '--button-padding-x': rem(30),
        '--button-fz': rem(24),
      },
    };
  }

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

  return { root: {} };
};

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

New variants system

すべてのコンポーネントがルート要素にdata-variant属性を持つようになりました。同じvariantを持つすべてのコンポーネントにスタイルを適用するために使用できます。

Demo.module.css
.input {
  &[data-variant='underline'] {
    border-bottom: rem(2px) solid;
    border-radius: 0;
    padding-left: 0;
    padding-right: 0;

    @mixin light {
      border-color: var(--mantine-color-gray-3);
    }

    @mixin dark {
      border-color: var(--mantine-color-dark-3);
    }

    &:focus {
      border-color: var(--mantine-color-blue-filled);
    }
  }
}
Demo.tsx
import { Input, MantineProvider, createTheme } from '@mantine/core';
import classes from './Demo.module.css';

const theme = createTheme({
  components: {
    Input: Input.extend({ classNames: classes }),
  }
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <Input variant="underline" placeholder="Underline input" />
      <Input variant="filled" placeholder="Filled input" mt="md" />
    </MantineProvider>
  );
}

New sizes system

コンポーネントのsizeのカスタマイズ方法は複数存在します。

  • data-size属性
  • CSS variables
  • static CSS variables

以下の例はdata-size属性でサイズをカスタマイズする方法になります。

Demo.module.css
.wrapper {
  &[data-size='xxl'] {
    & .input {
      padding-left: rem(28px);
      padding-right: rem(28px);
      height: rem(68px);
      font-size: rem(28px);
    }
  }

  &[data-size='xxs'] {
    & .input {
      padding-left: rem(10px);
      padding-right: rem(10px);
      height: rem(28px);
      font-size: rem(10px);
    }
  }
}
Demo.tsx
import { Input, createTheme, MantineProvider } from '@mantine/core';
import classes from './Demo.module.css';

const theme = createTheme({
  components: {
    Input: Input.extend({ classNames: classes }),
  },
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <Input placeholder="Size XXL" size="xxl" />
      <Input placeholder="Size XXS" size="xxs" mt="md" />
    </MantineProvider>
  );
}

theme.variantColorResolver

theme.variantColorResolver を使って、デフォルトのvariantで扱う色をカスタマイズしたり、新しいvariantのサポートを追加したりすることができます。theme.variantColorResolverは以下のプロパティを持つオブジェクトを返さなければなりません。

interface VariantColorResolverResult {
  background: string;
  hover: string;
  color: string;
  border: string;
}

以下はデフォルトのvariantで扱う色のカスタムと新しいvariantのサポート追加の例です。

import {
  Button,
  Group,
  MantineProvider,
  defaultVariantColorsResolver,
  VariantColorsResolver,
  parseThemeColor,
  rem,
  rgba,
  darken,
} from '@mantine/core';

const variantColorResolver: VariantColorsResolver = (input) => {
  const defaultResolvedColors = defaultVariantColorsResolver(input);
  const parsedColor = parseThemeColor({
    color: input.color || input.theme.primaryColor,
    theme: input.theme,
  });

  // プロパティの上書き
  if (parsedColor.isThemeColor && parsedColor.color === 'lime' && input.variant === 'filled') {
    return { ...defaultResolvedColors, color: 'var(--mantine-color-black)' };
  }

  // 完全に上書き
  if (input.variant === 'light') {
    return {
      background: rgba(parsedColor.value, 0.1),
      hover: rgba(parsedColor.value, 0.15),
      border: `${rem(1)} solid ${parsedColor.value}`,
      color: darken(parsedColor.value, 0.1),
    };
  }

  // 新しいvariantの追加
  if (input.variant === 'danger') {
    return {
      background: 'var(--mantine-color-red-9)',
      hover: 'var(--mantine-color-red-8)',
      color: 'var(--mantine-color-white)',
      border: 'none',
    };
  }

  return defaultResolvedColors;
};

function Demo() {
  return (
    <MantineProvider theme={{ variantColorResolver }}>
      <Group>
        <Button color="lime.4" variant="filled">
          Lime filled button
        </Button>

        <Button color="orange" variant="light">
          Orange light button
        </Button>

        <Button variant="danger">Danger button</Button>
      </Group>
    </MantineProvider>
  );
}

rem units scaling

rem単位の拡大縮小が可能になりました。html/:root要素のフォントサイズを変更し、Mantineコンポーネントのサイズを保持したい場合に便利です。例えば、htmlのフォントサイズを10pxに設定した場合、scaleを1 / (10 / 16)= 1 / 0.625 = 1.6に設定するとコンポーネントのサイズを保持できます。

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

const theme = createTheme({
  scale: 1.6,
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <App />
    </MantineProvider>
  );
}

color prop improvements

カラーpropをサポートするすべてのコンポーネントが、以下のカラー値をサポートするようになりました。

  • theme.colorsのキー。例えばblue
  • theme.colorsのインデックス参照。例えばblue.5
  • 有効な色を表す値。例えば #fff, rgba(0, 0, 0, 0.5), hsl(0, 0%, 100%)

Components classes

各コンポーネントのクラスは、Component.classesオブジェクトで利用できるようになりました。例えば、ButtonのクラスはButton.classesにあります。これらのクラスを使用して、Mantineコンポーネントと同じスタイルのコンポーネントを作成できます。

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

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

Colors generator

新しい@mantine/colors-generatorパッケージは、単一のカラー値に基づいてカラーパレットを生成するために利用可能になりました。オンラインツールとしても利用できます。コントラストの問題を避けるためにも通常は事前に色を生成しておく方がよいでしょう。

New setup for RTL

mantine/coreパッケージはDirectionProviderコンポーネントをエクスポートするようになりました。すべてのコンポーネントがデフォルトでRTLスタイルを含むようになりました。DirectionProviderの設定でコンポーネントの向きを変えられるようです。

詳しくはRTLのドキュメントをご覧ください。
https://v7.mantine.dev/styles/rtl

React 18+ only

バージョン7.0から、Mantineは古いバージョンのReactをサポートしなくなりました。最小サポートバージョンは React 18 になりました。これは Mantine コンポーネントが useId と useSyncExternalStore フックを使用するようになり、React 18 でのみ使用できるようになったためです。

left and right section

以前rightSectionとiconのプロップを持っていたコンポーネントは、iconの代わりにleftSectionを使うようになりました。

withErrorStyles prop

全てのInputコンポーネントでwithErrorStyles propのサポートがされた。error propが設定されているときにテキストとボーダーの色を赤くするかどうか設定できます。デフォルトではtrue。

hiddenFrom and visibleFrom props

すべてのMantineコンポーネントがhiddenFromとvisibleFromプロップをサポートするようになりました。これらのプロップはブレークポイント(xs, sm, md, lg, xl)を受け取り、ビューポートの幅が指定されたブレークポイントにより大きいか小さいかでコンポーネントの表示を切り替えることができます。

まとめ

今回の記事は以上になります。大きな変更もありv6からv7にアップデートするためには少し対応に時間が必要かなと思いました!
もっと詳しく知りたいと思った方は是非公式ドキュメントを読んでみてください!

Discussion