使用高级 Chakra UI 组件更快地构建 💎

了解更多

在配方中处理动态样式

2025年1月3日

假设你需要根据按钮的按下状态来改变其内边距(padding)。

const App = () => {
  const [isPressed, setPressed] = useState(false)
  // How do style the button separately based on the pressed state?
  return <Button>Click Me</Button>
}

你可能会尝试这样做

import { defineRecipe } from "@chakra-ui/react"

export const buttonRecipe = defineRecipe({
  base: {
    display: "flex",
  },
  variants: {
    size: {
      sm: ({ isPressed }) => ({
        padding: isPressed ? "8" : "4",
        fontSize: "12px",
      }),
    },
  },
})

这不起作用,因为 Chakra 不支持在配方中使用函数。我们要求配方是可序列化的。

有两种方法可以处理这个问题

使用 data-* 属性

首先,使用 data-* 属性将动态值应用于组件。

const App = () => {
  const [isPressed, setPressed] = useState(false)
  return <Button data-pressed={isPressed || undefined}>Click Me</Button>
}

接下来,使用 data-* 属性为配方设置样式。

export const buttonRecipe = defineRecipe({
  base: {
    display: "flex",
  },
  variants: {
    size: {
      sm: {
        padding: "4",
        fontSize: "12px",
        "&[data-pressed]": {
          padding: "8",
        },
      },
    },
  },
})

使用 compoundVariants

复合变体允许你根据变体组合创建样式覆盖。

import { defineRecipe } from "@chakra-ui/react"

export const buttonRecipe = defineRecipe({
  base: {
    display: "flex",
  },
  variants: {
    size: {
      sm: {
        padding: "4",
        fontSize: "12px",
      },
    },
    isPressed: {
      true: {},
      false: {},
    },
  },
  compoundVariants: [
    {
      size: "sm",
      isPressed: true,
      css: {
        padding: "8px",
        fontSize: "12px",
      },
    },
  ],
})

然后,你可以将 isPressed 变体作为 props 传递给组件。

<Button visual="solid" isPressed={isPressed}>
  Click Me
</Button>
注意
如果你使用 TypeScript,别忘了运行 npx @chakra-ui/cli typegen 命令来为配方生成类型。