自定义组件

了解如何使用 Tailwind Variants API 自定义 Nuxt UI 组件,实现高级、灵活且可维护的样式。

Tailwind Variants

Nuxt UI 组件的样式是使用Tailwind VariantsAPI 定义的,它提供了一种强大的方式来创建变体并管理组件样式。

插槽

组件可以有多个 slots,每个 slot 代表组件中一个独立的 HTML 元素或部分。这些 slot 允许灵活的内容插入和样式设置。

我们以 Card 组件为例,它有多个 slot

export default {
  slots: {
    root: 'bg-default ring ring-default divide-y divide-default rounded-lg',
    header: 'p-4 sm:px-6',
    body: 'p-4 sm:p-6',
    footer: 'p-4 sm:px-6'
  }
}

有些组件没有 slot,它们只由一个根元素组成。在这种情况下,主题只定义了 base slot,例如 Container 组件

export default {
  base: 'max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8'
}
没有 slot 的组件没有ui 属性,只有class 属性可用于覆盖样式。

变体

组件支持 variants,它允许您根据组件 props 动态调整不同 slots 的样式。

例如,Avatar 组件使用 size 变体来控制其外观

src/theme/avatar.ts
export default {
  slots: {
    root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-elevated',
    image: 'h-full w-full rounded-[inherit] object-cover'
  },
  variants: {
    size: {
      sm: {
        root: 'size-7 text-sm'
      },
      md: {
        root: 'size-8 text-base'
      },
      lg: {
        root: 'size-9 text-lg'
      }
    }
  },
  defaultVariants: {
    size: 'md'
  }
}

通过这种方式,size prop 将相应的样式应用于 root slot

<template>
  <UAvatar src="https://github.com/nuxt.png" size="lg" />
</template>

默认变体

defaultVariants 属性设置每个变体在没有传递 prop 时的默认值。

例如,Avatar 组件的默认大小设置为 md

src/theme/avatar.ts
export default {
  slots: {
    root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-elevated',
    image: 'h-full w-full rounded-[inherit] object-cover'
  },
  variants: {
    size: {
      sm: {
        root: 'size-7 text-sm'
      },
      md: {
        root: 'size-8 text-base'
      },
      lg: {
        root: 'size-9 text-lg'
      }
    }
  },
  defaultVariants: {
    size: 'md'
  }
}
您可以在 nuxt.config.ts 中使用 theme.defaultVariants 选项,一次性覆盖所有组件的 sizecolor 默认值。
您可以在 vite.config.ts 中使用 theme.defaultVariants 选项,一次性覆盖所有组件的 sizecolor 默认值。

复合变体

有些组件使用 compoundVariants 属性,当多个变体条件同时满足时应用类。

例如,Button 组件使用 compoundVariants 属性,为特定的 colorvariant 组合应用类

src/theme/button.ts
import type { ModuleOptions } from '../module'

export default (options: Required<ModuleOptions>) => ({
  slots: {
    base: ['rounded-md font-medium inline-flex items-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75', options.theme.transitions && 'transition-colors']
  },
  variants: {
    color: {
      ...Object.fromEntries((options.theme.colors || []).map((color: string) => [color, ''])),
      neutral: ''
    },
    variant: {
      solid: '',
      outline: '',
      soft: '',
      subtle: '',
      ghost: '',
      link: ''
    }
  },
  compoundVariants: [
    ...(options.theme.colors || []).map((color: string) => ({
      color,
      variant: 'outline',
      class: `ring ring-inset ring-${color}/50 text-${color} hover:bg-${color}/10 active:bg-${color}/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-${color}`
    })),
    {
      color: 'neutral',
      variant: 'outline',
      class: 'ring ring-inset ring-accented text-default bg-default hover:bg-elevated active:bg-elevated disabled:bg-default aria-disabled:bg-default focus:outline-none focus-visible:ring-2 focus-visible:ring-inverted'
    }
  ],
  defaultVariants: {
    color: 'primary',
    variant: 'solid'
  }
})

自定义主题

您可以通过多种方式自定义 Nuxt UI 组件的外观,您可以一次性为所有组件进行自定义,也可以按组件进行自定义。

Tailwind Variants 在底层使用tailwind-merge来合并类,因此您不必担心类冲突。
您可以通过两种方式探索每个组件的主题
  • 查看每个独立组件文档中的 Theme 部分。
  • 直接在 GitHub 存储库中浏览源代码,路径为src/theme.

全局配置

您可以使用与主题对象完全相同的结构,在 app.config.ts 中全局覆盖组件主题。

您可以使用与主题对象完全相同的结构,在 vite.config.ts 中全局覆盖组件主题。

您可以自定义组件的slots, variants, compoundVariantsdefaultVariants来更改组件的默认主题

app.config.ts
export default defineAppConfig({
  ui: {
    button: {
      slots: {
        base: 'font-bold'
      },
      variants: {
        size: {
          md: {
            leadingIcon: 'size-4'
          }
        }
      },
      compoundVariants: [{
        color: 'neutral',
        variant: 'outline',
        class: 'ring-default hover:bg-accented'
      }],
      defaultVariants: {
        color: 'neutral',
        variant: 'outline'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        button: {
          slots: {
            base: 'font-bold'
          },
          variants: {
            size: {
              md: {
                leadingIcon: 'size-4'
              }
            }
          },
          compoundVariants: [{
            color: 'neutral',
            variant: 'outline',
            class: 'ring-default hover:bg-accented'
          }],
          defaultVariants: {
            color: 'neutral',
            variant: 'outline'
          }
        }
      }
    })
  ]
})
在这个示例中,font-bold 覆盖了所有按钮上的 font-medium,当 size="md" 时,size-4 覆盖了前导图标上的 size-5 类,当 color="neutral"variant="outline" 时,ring-default hover:bg-accented 覆盖了 ring-accented hover:bg-elevated。按钮现在默认为 color="neutral"variant="outline"

ui 属性

您还可以使用 ui prop 覆盖组件的 slots。这优先于全局配置和解析的 variants

<template>
  <UButton
    trailing-icon="i-lucide-chevron-right"
    size="md"
    color="neutral"
    variant="outline"
    :ui="{
      trailingIcon: 'rotate-90 size-3'
    }"
  >
    Button
  </UButton>
</template>
在此示例中,trailingIcon slot 被 size-3 覆盖,尽管 md 尺寸变体将对其应用 size-5 类。

class 属性

class prop 允许您覆盖 rootbase slot 的类。这优先于全局配置和解析的 variants

<template>
  <UButton class="font-bold rounded-full">Button</UButton>
</template>
在此示例中,font-bold 类将覆盖此按钮上的默认 font-medium 类。