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

了解更多
跳到内容
文档演练场指南博客
赞助商

槽位样式(Slot Recipes)

了解如何使用槽位样式(slot recipes)为多部分组件设置样式。

概述

当您需要对组件的多个部分应用样式变体时,槽位样式(Slot Recipes)会非常方便。

一个槽位样式包含以下属性:

  • className: 要附加到组件槽位的 className 前缀
  • slots: 要设置样式的组件部分数组
  • base: 每个槽位的基本样式
  • variants: 每个槽位的不同视觉样式
  • defaultVariants: 组件的默认变体
  • compoundVariants: 每个槽位的复合变体组合和样式覆盖。

定义样式

使用 defineSlotRecipe 标识函数来创建槽位样式。

checkbox.recipe.ts

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

export const checkboxSlotRecipe = defineSlotRecipe({
  slots: ["root", "control", "label"],
  base: {
    root: { display: "flex", alignItems: "center", gap: "2" },
    control: { borderWidth: "1px", borderRadius: "sm" },
    label: { marginStart: "2" },
  },
  variants: {
    size: {
      sm: {
        control: { width: "8", height: "8" },
        label: { fontSize: "sm" },
      },
      md: {
        control: { width: "10", height: "10" },
        label: { fontSize: "md" },
      },
    },
  },
})

使用样式

有两种方式在组件中使用样式:

  • 直接在组件中使用 useSlotRecipe
  • 作为复合组件(推荐)使用 createSlotRecipeContext
信息
使用 useSlotRecipe 钩子或 createSlotRecipeContext 函数需要添加 "use client" 指令。这是因为它们底层依赖于 React 钩子,例如 useContextuseInsertionEffect

直接在组件中

使用 useSlotRecipe 钩子获取组件的样式。然后,使用其变体属性调用样式以获取样式。

checkbox.tsx

"use client"

import { chakra, useSlotRecipe } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

export const Checkbox = (props) => {
  const { size, ...restProps } = props

  const recipe = useSlotRecipe({ recipe: checkboxSlotRecipe })
  const styles = recipe({ size })

  return (
    <chakra.label css={styles.root}>
      <chakra.input type="checkbox" css={styles.control} {...restProps} />
      <chakra.span css={styles.label}>Checkbox Label</chakra.span>
    </chakra.label>
  )
}

splitVariantProps

请注意,size 属性是如何从属性中解构出来并传递给样式的。更智能的方法是自动将样式属性从组件属性中分离出来。

为此,请使用 recipe.splitVariantProps 函数将样式属性从组件属性中分离出来。

checkbox.tsx

"use client"

import { chakra, useSlotRecipe } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

export const Checkbox = (props) => {
  const recipe = useSlotRecipe({ recipe: checkboxSlotRecipe })
  const [recipeProps, restProps] = recipe.splitVariantProps(props)
  const styles = recipe(recipeProps)

  //...
}

TypeScript

要推断样式变体属性类型,请使用 RecipeVariantProps 类型助手。

checkbox.tsx

import type { RecipeVariantProps } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

type CheckboxVariantProps = RecipeVariantProps<typeof checkboxSlotRecipe>

export interface CheckboxProps
  extends React.PropsWithChildren<CheckboxVariantProps> {}

创建复合组件

将样式传递给 createSlotRecipeContext 函数以创建槽位样式上下文。

然后,使用 withProviderwithContext 函数创建共享相同上下文的复合组件。

信息
您需要手动为 withProviderwithContext 键入泛型。此方法旨在优化 TypeScript 性能。自动推断虽然方便,但由于涉及类型的复杂性,会减慢 TypeScript 编译速度。

checkbox.tsx

"use client"

import { createSlotRecipeContext } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

const { withProvider, withContext } = createSlotRecipeContext({
  recipe: checkboxSlotRecipe,
})

interface CheckboxRootProps
  extends HTMLChakraProps<
    "label",
    RecipeVariantProps<typeof checkboxSlotRecipe>
  > {}
export const CheckboxRoot = withProvider<HTMLLabelElement, CheckboxRootProps>(
  "label",
  "root",
)

interface CheckboxControlProps extends HTMLChakraProps<"input"> {}
export const CheckboxControl = withContext<
  HTMLInputElement,
  CheckboxControlProps
>("input", "control")

interface CheckboxLabelProps extends HTMLChakraProps<"span"> {}
export const CheckboxLabel = withContext<HTMLSpanElement, CheckboxLabelProps>(
  "span",
  "label",
)

将变体属性传递给“根”组件以应用样式。

注意:根组件是使用 withProvider 函数的组件。

app.tsx

const App = () => {
  return (
    <CheckboxRoot size="md">
      <CheckboxControl />
      <CheckboxLabel />
    </CheckboxRoot>
  )
}

unstyled 属性

此方法支持使用 unstyled 属性来移除样式所应用的样式。

checkbox.tsx

<CheckboxRoot unstyled>
  <CheckboxControl />
  <CheckboxLabel />
</CheckboxRoot>

TypeScript

要推断样式变体属性类型,请使用 RecipeVariantProps 类型助手。

import type { RecipeVariantProps, UnstyledProp } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

type CheckboxVariantProps = RecipeVariantProps<typeof checkboxSlotRecipe>

export interface CheckboxProps
  extends React.PropsWithChildren<CheckboxVariantProps>,
    UnstyledProp {}

复合变体

使用 compoundVariants 属性定义一组基于其他变体组合应用的变体。

checkbox.recipe.ts

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

export const checkboxRecipe = defineSlotRecipe({
  slots: ["root", "control", "label"],
  base: {},
  variants: {
    size: {
      sm: {},
      md: {},
    },
    visual: {
      contained: {},
      outline: {},
    },
  },
  compoundVariants: [
    {
      size: "sm",
      visual: "outline",
      css: {
        control: { borderWidth: "1px" },
        label: { color: "green.500" },
      },
    },
  ],
})

定位槽位

在某些情况下,可能需要按 className 定位槽位。

  • 在配置中设置 className 属性
  • 命名约定是 ${className}__${slot}

checkbox.recipe.ts

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

export const checkboxRecipe = defineSlotRecipe({
  className: "checkbox",
  slots: ["root", "control", "label"],
  base: {
    root: {
      bg: "blue.500",
      _hover: {
        "& .checkbox__label": { color: "white" },
      },
    },
  },
})

主题使用

为了以可重用方式使用样式,将其移动到系统主题并将其添加到 theme.slotRecipes 属性。

在主题中使用样式时,无需添加 "use client" 指令。

theme.ts

import { createSystem, defaultConfig, defineConfig } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

const config = defineConfig({
  theme: {
    slotRecipes: {
      checkbox: checkboxSlotRecipe,
    },
  },
})

export default createSystem(defaultConfig, config)

TypeScript

使用 CLI 为样式生成类型。

npx @chakra-ui/cli typegen ./theme.ts

然后,在您的组件中导入生成的类型。

checkbox.tsx

import type { SlotRecipeProps, UnstyledProp } from "@chakra-ui/react"

export interface CheckboxProps
  extends SlotRecipeProps<"checkbox">,
    UnstyledProp {}

更新代码

如果您直接在组件中使用样式,请更新 useRecipe 以使用 key 属性从主题中获取样式。

checkbox.tsx

const Checkbox = () => {
-  const recipe = useRecipe({ recipe: checkboxRecipe })
+  const recipe = useRecipe({ key: "checkbox" })
  // ...
}

如果您创建复合组件,请更新 createSlotRecipeContext 以使用 key 属性。

checkbox.tsx

const { withProvider, withContext } = createSlotRecipeContext({
-  recipe: checkboxRecipe,
+  key: "checkbox",
})

上一页

样式(Recipes)

下一页

动画