Closed6

chakra-ui themingで複数部品からなるカスタムコンポーネントを作成

HaruHaru

2のMultiPart componentsとして、Card コンポーネントをつくる。
完成イメージ

function Usage() {
  return (
    <Card>
      <CardTitle>タイトル</CardTitle>
      <CardDescription>詳細な説明</CardDescription>
    </Card>
  )
}
HaruHaru

Themingによるスタイル定義

export const Card: ComponentMultiStyleConfig = {
  parts: ['card', 'cardTitle', 'cardDescription'],
  baseStyle: {
    card: {
      shadow: 'md',
      borderWidth: '1px',
      borderRadius: 'md',
    },
    cardTitle: {
      textAlign: 'center',
      fontWeight: 'bold',
      fontSize: '2xl',
    },
    cardDescription: {
      color: 'gray.600',
    },
  },
  sizes: {
    sm: {
      card: {
        padding: 2,
      },
      cardTitle: {
        marginBottom: 2,
      },
    },
    md: {
      card: {
        padding: 6,
      },
      cardTitle: {
        marginBottom: 6,
      },
    },
  },

  // 今回は使用しない
  variants: {},

  defaultProps: {
    size: 'md',
  },
};
HaruHaru

Card

interface CardProps extends BoxProps {
  size?: 'sm' | 'md';
}

export const Card: React.FC<CardProps> = (props) => {
  const {children, size, ...boxProps} = props;
  const styles = useMultiStyleConfig('Card', {size});

  return (
    <Box __css={styles.card} {...boxProps}>
      <StylesProvider value={styles}>{children}</StylesProvider>
    </Box>
  );
};

CardTitle

interface CardTitleProps extends BoxProps {}

export const CardTitle: React.FC<CardTitleProps> = (props) => {
  const styles = useStyles();

  return <Box as="h2" __css={styles.cardTitle} {...props} />;
};

CardDescription

export interface CardDescriptionProps extends BoxProps {}

export const CardDescription: React.FC<CardDescriptionProps> = (props) => {
  const styles = useStyles();

  return <Box __css={styles.cardDescription} {...props} />;
};
HaruHaru
function Usage() {
  return (
    <Card size='sm'>
      <CardTitle>カードのタイトル</CardTitle>
      <CardDescription>カードの詳細な説明_...</CardDescription>
    </Card>
  )
}

size: md

size: sm

今回の定義では、sizeを変更することにより一部paddingやmarginが変更される。
例えば、画面サイズによってmobile表示時にspaceを詰めたいときなどに使用できるようになるなど。

// 例
const size = isMobile ? 'sm' : 'md'
HaruHaru

CardTitleについて

例えば、Headingをラップして作成することについてはどうだろうか?

// 既存コンポーネントである、Heading
// @see https://chakra-ui.com/docs/components/typography/heading
import {Heading} from 'chakra-ui';

export const CardTitle: React.FC<CardTitleProps> = (props) => {
  const styles = useStyles();

  return <Heading __css={styles.cardTitle} {...props} />;
};

こちらの実装では、 __css は動作しない。
Headingに関しては、カスタムコンポーネントではなく既存コンポーネントであるため。

あくまで全体のHeadingの定義をThemingによって上書きする、ということは可能。

どうしてもHeadingを使用したい場合は、下記のようにpropsで上書きすることになる。
ただ下記だとsize違いの定義なども不便になるので、基本的には上述のやり方がおすすめ。

export const CardTitle: React.FC<CardTitleProps> = (props) => {
  return <Heading fontSize="2xl" textAlign="center" {...props} />;
};

Issueでも同様の質問が上がっていた
https://github.com/chakra-ui/chakra-ui/issues/5747

このスクラップは2023/01/23にクローズされました