Skip to content

Button Component ​

A versatile button component built with TypeScript and styled with Tailwind CSS. Supports multiple variants, sizes, and states for comprehensive UI interactions.

Overview ​

The Button component provides a consistent interface element with multiple styling variants, sizes, and accessibility features built-in. It follows the shadcn/ui design system patterns.

Preview ​

Basic Usage ​

vue
<template>
  <div class="space-x-4">
    <!-- Default button -->
    <Button label="Click me" variant="default" size="medium" />
    
    <!-- Success button with icon -->
    <Button label="Success" variant="success" icon="heroicons:check" size="medium" />
    
    <!-- Warning button -->
    <Button label="Warning" variant="warning" size="medium" />
    
    <!-- Glassmorphism button -->
    <Button label="Glass Effect" variant="glassmorphism" size="medium" />
    
    <!-- Disabled button -->
    <Button label="Disabled" variant="default" size="medium" :disabled="true" />
  </div>
</template>

<script setup lang="ts">
import Button from '@/components/Button.vue'
</script>

Component API ​

Props ​

PropTypeDefaultDescription
labelstringRequiredButton text content
variant'success' | 'warning' | 'default' | 'glassmorphism''default'Button visual style variant
size'small' | 'medium' | 'large''medium'Button size
disabledbooleanfalseDisable button interaction
classNamestring''Additional CSS classes
iconstringundefinedIcon name from Iconify collection

Events ​

The Button component supports all native button events through v-bind="$attrs":

  • @click - Click event handler
  • @focus - Focus event handler
  • @blur - Blur event handler
  • @mouseenter - Mouse enter event
  • @mouseleave - Mouse leave event

Slots ​

Ce composant n'utilise pas de slots. Le contenu est défini via la prop label et les icônes via la prop icon.

Variants ​

Default ​

Transparent button with border styling, perfect for secondary actions.

vue
<Button label="Default Action" variant="default" size="medium" />

Styles :

  • Light: bg-transparent hover:bg-gray-100 avec bordures grises
  • Dark: dark:bg-transparent dark:hover:bg-gray-800 avec bordures sombres

Success ​

Green button for positive actions (save, confirm, etc.).

vue
<Button label="Save Changes" variant="success" icon="heroicons:check" size="medium" />

Styles :

  • Light: bg-green-500 hover:bg-green-600 avec ombres
  • Dark: dark:bg-green-600 dark:hover:bg-green-700

Warning ​

Red button for dangerous or warning actions (delete, remove, etc.).

vue
<Button label="Delete Account" variant="warning" icon="heroicons:trash" size="medium" />

Styles :

  • Light: bg-red-300 hover:bg-red-400 avec ombres
  • Dark: dark:bg-red-400 dark:hover:bg-red-500

Glassmorphism ​

Modern glass effect button with backdrop blur and transparency.

vue
<Button label="Glass Effect" variant="glassmorphism" size="medium" />

Styles :

  • Light: bg-white/20 backdrop-blur-md avec bordures semi-transparentes
  • Dark: dark:bg-gray-900/20 avec effet de flou et transparence

Sizes ​

Small ​

Compact button for secondary actions or tight spaces.

vue
<Button label="Small" variant="default" size="small" />

Styles : px-3 py-1.5 text-sm

Medium ​

Standard button size for most use cases.

vue
<Button label="Medium" variant="success" size="medium" />

Styles : px-4 py-2 text-base

Large ​

Prominent button for primary actions.

vue
<Button label="Large" variant="warning" size="large" />

Styles : px-6 py-3 text-lg

Advanced Examples ​

With Icons ​

vue
<template>
  <div class="space-x-4">
    <!-- Success with check icon -->
    <Button label="Save" variant="success" icon="heroicons:check" size="medium" />
    
    <!-- Warning with trash icon -->
    <Button label="Delete" variant="warning" icon="heroicons:trash" size="medium" />
    
    <!-- Default with plus icon -->
    <Button label="Add Item" variant="default" icon="heroicons:plus" size="medium" />
    
    <!-- Glassmorphism with star icon -->
    <Button label="Favorite" variant="glassmorphism" icon="heroicons:star" size="medium" />
  </div>
</template>

Loading State ​

vue
<template>
  <Button 
    :label="isLoading ? 'Loading...' : 'Submit'" 
    :icon="isLoading ? 'heroicons:arrow-path' : 'heroicons:paper-airplane'"
    variant="success" 
    size="medium" 
    :disabled="isLoading"
    :className="isLoading ? 'animate-pulse' : ''"
  />
</template>

<script setup lang="ts">
const isLoading = ref(false)

const handleSubmit = async () => {
  isLoading.value = true
  try {
    // Your async operation
    await submitForm()
  } finally {
    isLoading.value = false
  }
}
</script>

Form Integration ​

vue
<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="email" type="email" placeholder="Enter email" />
    
    <div class="flex gap-4 mt-4">
      <Button 
        label="Submit" 
        variant="success" 
        icon="heroicons:paper-airplane"
        size="medium"
        :disabled="!email"
      />
      <Button 
        label="Reset" 
        variant="default" 
        icon="heroicons:arrow-path"
        size="medium"
        @click="reset"
      />
    </div>
  </form>
</template>

Styling Details ​

CSS Classes Structure ​

The button uses a combination of base classes and variant-specific classes:

typescript
// Base classes (always applied)
'transition-all duration-300 flex items-center justify-center rounded-md px-4 py-2 font-medium'

// Variant classes (applied based on variant prop)
variants = {
  success: 'bg-green-500 hover:bg-green-600 text-white shadow-lg hover:shadow-xl dark:bg-green-600 dark:hover:bg-green-700',
  warning: 'bg-red-300 hover:bg-red-400 text-white shadow-lg hover:shadow-xl dark:bg-red-400 dark:hover:bg-red-500',
  default: 'bg-transparent hover:bg-gray-100 text-gray-700 border border-gray-300 hover:border-gray-400 dark:bg-transparent dark:hover:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:border-gray-500',
  glassmorphism: 'bg-white/20 backdrop-blur-md border border-white/30 text-gray-800 hover:bg-white/30 hover:border-white/40 shadow-lg dark:bg-gray-900/20 dark:border-gray-700/30 dark:text-gray-200 dark:hover:bg-gray-900/30 dark:hover:border-gray-700/40'
}

// Size classes (applied based on size prop)
sizes = {
  small: 'px-3 py-1.5 text-sm',
  medium: 'px-4 py-2 text-base',
  large: 'px-6 py-3 text-lg'
}

Custom Styling ​

You can extend styling using the className prop:

vue
<Button 
  label="Custom Styled Button" 
  variant="default" 
  size="medium"
  className="border-2 border-dashed hover:border-solid shadow-xl"
/>

Accessibility ​

The Button component includes built-in accessibility features:

  • Keyboard Navigation - Full keyboard support with Enter and Space
  • Focus Management - Visible focus rings with focus-visible
  • Disabled State - Proper disabled handling with pointer-events-none
  • ARIA Support - Inherits all native button ARIA attributes

Screen Reader Support ​

vue
<!-- Button with screen reader label -->
<Button 
  label="Close" 
  variant="default" 
  icon="heroicons:x-mark"
  size="medium"
  aria-label="Close dialog"
/>

<!-- Button with description -->
<Button 
  label="Submit Form" 
  variant="success" 
  icon="heroicons:paper-airplane"
  size="medium"
  aria-describedby="button-help"
/>
<div id="button-help" class="sr-only">
  This will submit your form data
</div>

Utility Function ​

The component uses a utility function for class merging:

typescript
import { cn } from '@/lib/utils'

// cn() combines classes intelligently, handling conflicts
const className = cn(
  'base-classes',
  variants[variant],
  sizes[size],
  props.className
)

TypeScript Support ​

Full TypeScript integration with proper prop types:

typescript
interface Props {
  label: string
  variant: 'success' | 'warning' | 'default' | 'glassmorphism'
  size: 'small' | 'medium' | 'large'
  disabled?: boolean
  className?: string
  icon?: string
}

Dependencies ​

  • @nuxt/icon - Pour les icônes Iconify
  • Tailwind CSS - Pour le styling
  • Vue 3 - Composition API
  • TypeScript - Support des types
  • Toast - For user feedback after button actions
  • Modal - For confirmation dialogs

Built with love by mhdevfr