Composants personnalisés

Découvrez comment personnaliser les composants de l'interface utilisateur Nuxt avec l'API Tailwind Variants pour un style avancé, flexible et facile à maintenir.

Tailwind Variants

Les composants de l'interface utilisateur Nuxt sont stylisés à l'aide de l'API Tailwind Variants, qui fournit un moyen puissant de créer des variantes et de gérer les styles de composants.

Slots

Les composants peuvent avoir plusieurs slots, chacun représentant un élément ou une section HTML distincte au sein du composant.

Prenons l'exemple du composant Card qui possède plusieurs emplacements:

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'
  }
}
Les composants réels portent également des gardes v-if sur chaque emplacement et le thème définit les styles variant sur le dessus des emplacements.

En este caso, el tema solo define la ranura base como el componente Container por ejemplo:

export default {
  base: 'w-full max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8'
}
Il expose une seule clé base au lieu d'une clé par emplacement. Link et [Iconxph072/docs/components/icon) sont les exceptions: ils n'ont pas de prop ui, donc utilisez le [ph062x propxph077) pour remplacer leurs styles.

Variants

Les composants prennent en charge variants, qui ajustent dynamiquement les styles de différents slots en fonction des accessoires des composants.

Par exemple, le composant Avatar utilise une variante size pour contrôler son apparence:

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'
  }
}

De cette façon, le prop size appliquera les styles correspondants au slot root:

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

Default Variants par défaut

La propriété defaultVariants définit la valeur par défaut pour chaque variante lorsqu 'aucun prop n'est passé.

Par exemple, le composant Avatar a sa taille par défaut définie sur 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'
  }
}
Vous pouvez utiliser l'option theme.defaultVariants dans votre nuxt.config.ts pour remplacer les size et color par défaut de chaque composant qui utilise md et primary.
Vous pouvez utiliser l'option theme.defaultVariants dans votre vite.config.ts pour remplacer les size et color par défaut de chaque composant qui utilise md et primary.

Variantes composées

Certains composants utilisent la propriété compoundVariants pour appliquer des classes lorsque plusieurs conditions de variantes sont remplies en même temps.

Par exemple, le composant Button utilise la propriété compoundVariants pour appliquer des classes pour une combinaison spécifique de color et 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'
  }
})

Personnaliser thème

Vous avez plusieurs façons de personnaliser l'apparence des composants de l'interface utilisateur Nuxt, vous pouvez le faire pour tous les composants à la fois ou par composant.

Tailwind Variants utilise tailwind-merge sous le capot pour fusionner les classes afin que vous n'ayez pas à vous soucier des classes conflictuelles.
Vous pouvez explorer le thème de chaque composant de deux manières:Vérifiez la section Theme dans la documentation de chaque composant.
  • Parcourez le code source directement dans le dépôt GitHub à src/theme

Configuration globale

Vous pouvez remplacer le thème des composants globalement à l'intérieur de votre app.config.ts en utilisant exactement la même structure que l'objet thème.

You can override the theme of components globally inside your vite.config.ts by using the exact same structure as the theme object.

Vous pouvez personnaliser les xslots, , compoundVariants et defaultVariants d'un composant pour modifier le thème par défaut d'un composant:

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'
          }
        }
      }
    })
  ]
})
Dans cet exemple, font-bold remplace font-medium sur tous les boutons, size-4 remplace la classe size-5 sur l'icône principale quand size="md" et ring-default hover:bg-accented remplace ring-accented hover:bg-elevated quand color="neutral" et variant="outline".

Par défaut, ces classes sont fusionnées sur les valeurs par défaut du composant. Vous pouvez également définir un emplacement pour une fonction pour les remplacer à la place. Il reçoit les classes par défaut de l'emplacement comme argument, de sorte que vous pouvez réutiliser une partie d'entre elles. Les classes de variants et compoundVariants s'appliquent toujours en haut, exactement comme lorsque vous passez une chaîne.

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'
          }
        }
      }
    })
  ]
})
Définissez un emplacement pour une fonction à un seul endroit. Nuxt fusionne le app.config.ts de chaque layer avec defuFn, qui appelle votre fonction avec la valeur que la clé a déjà plus bas et ne conserve que le résultat. Nuxt UI lit ensuite une chaîne de caractères simple, de sorte que ses classes sont fusionnées sur les valeurs par défaut au lieu de les remplacer.
Pour supprimer les classes par défaut de tous les composants à la fois, utilisez l'option theme.unstyled dans votre nuxt.config.ts.
Pour supprimer les classes par défaut de tous les composants en même temps, utilisez l'option theme.unstyled dans votre vite.config.ts.

Composant ### Theme

Le composant Theme remplace les slots et les valeurs par défaut de prop pour tous ses descendants, sans affecter le reste de l'application. mais les accessoires ui et class l'emportent toujours. Les machines à sous acceptent la même forme de fonction pour remplacer leurs classes au lieu de fusionner. Elle est résolue au même point que le ui prop, il se comporte donc de la même manière.

<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 prop

Vous pouvez également remplacer slots d'un composant en utilisant la prop. ui Ceci a la priorité sur la configuration globale et variants résolu.

<template>
  <UButton
    trailing-icon="i-lucide-chevron-right"
    size="md"
    color="neutral"
    variant="outline"
    :ui="{
      trailingIcon: 'rotate-90 size-3'
    }"
  >
    Button
  </UButton>
</template>
Dans cet exemple, l'emplacement trailingIcon est écrasé par size-3, même si la variante de taille md lui appliquerait une classe size-5.

Une valeur de slot peut aussi être une fonction pour remplacer ses classes au lieu de les fusionner:

<template>
  <UButton :ui="{ label: () => 'text-base font-bold' }" label="Button" />
</template>
La prop ui est résolue après la prop variants, donc contrairement à la configuration globale, une fonction remplace ici les classes resolved de l'emplacement. Son argument contient ce que les classes variants et compoundVariants ont correspondu. Une prop class sur le même emplacement est toujours fusionnée au-dessus des classes retournées.

xclass prop

Utilisez la prop class pour remplacer les classes de l'emplacement root ou base. Ceci a la priorité sur la configuration globale et le variants résolu.

<template>
  <UButton class="font-bold rounded-full">Button</UButton>
</template>
Dans cet exemple, la classe font-bold remplacera la classe font-medium par défaut sur ce bouton.