コンポーネントのカスタマイズ

Tailwind Variants APIを使用してNuxt UIコンポーネントをカスタマイズし、高度で柔軟で保守性の高いスタイリングを実現する方法を学びます。

Tailwindバリアント

Nuxt UIコンポーネントはTailwind Variants APIを使用してスタイリングされます。

スロット

コンポーネントは複数のslotsを持つことができ、それぞれがコンポーネント内の個別のHTML要素またはセクションを表します。これらのスロットは柔軟なコンテンツの挿入とスタイルを可能にします。

複数のスロットを持つ例としてCardコンポーネントを見てみましょう:

export default {
  slots: {
    root: 'rounded-lg overflow-hidden',
    header: 'p-4 sm:px-6',
    title: 'text-highlighted font-semibold',
    description: 'mt-1 text-muted text-sm',
    body: 'p-4 sm:p-6',
    footer: 'p-4 sm:px-6'
  }
}
上記のスニペットは要約されています。実際のコンポーネントも各スロットにv-ifガードを搭載し、テーマはスロットの上にvariantスタイルを定義します。

一部のコンポーネントはスロットを持たず、単一のルート要素で構成されています。この場合、テーマはContainerコンポーネントのようにbaseスロットのみを定義します。

export default {
  base: 'w-full max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8'
}
スロットを持たないコンポーネントはui propを持ちます。これはスロットごとに1つのキーではなく、1つのbaseキーを公開します。LinkとIconは例外ですuiプロパティを持たないので、[class prop](]()を使用してスタイルをオーバーライドします。

バリアント

コンポーネントは、コンポーネントのプロップに基づいて異なるslotsのスタイルを動的に調整するvariantsをサポートします。

例えば、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プロパティは対応するスタイルをrootスロットに適用します:

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

デフォルトバリアント

defaultVariantsプロパティは、プロパティが渡されない場合の各バリアントのデフォルト値を設定します。

例えば、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オプションを使用すると、mdとprimaryを使用するすべてのコンポーネントのデフォルトのsizeとcolorをオーバーライドできます。
vite.config.tsのtheme.defaultVariantsオプションを使用すると、mdとprimaryを使用するすべてのコンポーネントのデフォルトのsizeとcolorをオーバーライドできます。

Compoundバリアント

一部のコンポーネントはcompoundVariantsプロパティを使用して、複数のバリアント条件が同時に満たされた場合にクラスを適用します。

たとえば、ButtonコンポーネントはcompoundVariantsプロパティを使用して、特定のcolorとvariantの組み合わせにクラスを適用します。

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 outline-${color}/25 focus-visible:outline-3 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 outline-inverted/25 focus-visible:outline-3 focus-visible:ring-inverted'
    }
  ],
  defaultVariants: {
    color: 'primary',
    variant: 'solid'
  }
})

カスタマイズテーマ

Nuxt UIコンポーネントの外観をカスタマイズするには複数の方法があり、すべてのコンポーネントに対して一度に、またはコンポーネントごとにカスタマイズすることができます。

Tailwind Variantsはクラスをマージするためにtailwind-mergeを内部で使用するため、競合するクラスを心配する必要はありません。
各コンポーネントのテーマは、2つの方法で調べることができます。
  • 各コンポーネントのドキュメントのThemeセクションを確認してください。
  • GitHubリポジトリのsrc/themeでソースコードを直接参照してください。

Global設定

テーマオブジェクトとまったく同じ構造を使用することで、app.config.ts内のコンポーネントのテーマをグローバルにオーバーライドすることができます。

テーマオブジェクトとまったく同じ構造を使用することで、vite.config.ts内のコンポーネントのテーマをグローバルにオーバーライドすることができます。

コンポーネントのslots、variants、compoundVariants、xph2444xdefaultVariants](#default-variants)をカスタマイズして、コンポーネントのデフォルトテーマを変更できます。

app/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"とring-default hover:bg-accentedがcolor="neutral"とvariant="outline"をオーバーライドすると、size-4は先頭のアイコンのsize-5クラスをオーバーライドします。ボタンのデフォルトはcolor="neutral"とvariant="outline"です。

デフォルトでは、これらのクラスはコンポーネントのデフォルトにマージされます。代わりにそれらを置き換える関数にスロットを設定することもできます。スロットはスロットのデフォルトクラスを引数として受け取るので、その一部を再利用できます。variantsとcompoundVariantsのクラスは文字列を渡すときとまったく同じように、上に適用されます。

app/app.config.ts
export default defineAppConfig({
  ui: {
    button: {
      slots: {
        label: () => 'text-base font-bold'
      }
    }
  }
})
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: {
            label: () => 'text-base font-bold'
          }
        }
      }
    })
  ]
})
関数にスロットを1箇所だけに設定します。Nuxtはlayerのapp.config.tsをdefuFnとマージします。defuFnは、キーが既に持っている値を使って関数を呼び出し、結果のみを保持します。Nuxt UIはプレーン文字列を読み込むので、クラスはそれらを置き換えるのではなく、デフォルトにマージされます。
すべてのコンポーネントからデフォルトのクラスを一度に削除するには、nuxt.config.tsのtheme.unstyledオプションを使用します。
すべてのコンポーネントからデフォルトのクラスを一度に削除するには、vite.config.tsのtheme.unstyledオプションを使用します。

Themeコンポーネント

Themeコンポーネントは、アプリの残りの部分に影響を与えることなく、すべての子孫のスロットとプロパティのデフォルトをオーバーライドします。これはグローバル設定よりも優先されます。しかし、uiとclassのプロップはまだそれに勝っています。スロットはマージの代わりにクラスを置き換えるために同じ関数形式を受け入れます。uiと同じ時点で解決されます。propなので同じように振る舞います

<template>
  <UTheme
    :ui="{
      button: {
        base: 'rounded-full'
      }
    }"
  >
    <div class="flex items-center gap-2">
      <UButton label="Button" color="neutral" />
      <UButton label="Button" color="neutral" variant="outline" />
      <UButton label="Button" color="neutral" variant="subtle" />
    </div>
  </UTheme>
</template>

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>
この例では、mdサイズバリアントがsize-5クラスを適用するにもかかわらず、trailingIconスロットはsize-3で上書きされます。

スロット値はクラスをマージする代わりに置き換える関数でもあります:

<template>
  <UButton :ui="{ label: () => 'text-base font-bold' }" label="Button" />
</template>
uiプロパティはvariantsの後で解決されます。そのため、グローバル設定とは異なり、ここでは関数がスロットのresolvedクラスを置き換えます。その引数にはvariantsとcompoundVariantsがマッチしたものが含まれます。同じスロットのclassプロパティは、返されたクラスの上にマージされます。

classプロップ

classプロパティを使用して、rootまたはbaseスロットのクラスをオーバーライドします。これはグローバル設定と解決済みのvariantsの両方より優先されます。

<template>
  <UButton class="font-bold rounded-full">Button</UButton>
</template>
この例では、font-boldクラスはこのボタンのデフォルトのfont-mediumクラスをオーバーライドします。