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