Skip to content

ButtonBase Component

The ButtonBase component is a versatile button component designed with Vue 3 and Tailwind CSS. It includes customizable color options, size variants, and additional configuration options to adapt to different use cases.

Basic Usage

Import and use ButtonBase in your Vue components as follows:

vue
<template>
  <ButtonBase label="Click Me" />
</template>

<script setup>
import { ButtonBase } from '@robuust-digital/vue-components';
</script>

Props

Below are the props supported by ButtonBase, which allow you to customize its appearance and behavior:

PropTypeDefaultDescription
asString, Object, FunctionbuttonDefines the element type or component to render.
bindAsString''In some cases (e.g. when using Inertia links) where you need to render a different element, you can use this prop to bind the component to a different element.
labelString''Sets the text label for the button.
iconObject, FunctionnullSpecifies an icon to display within the button. @heroicons/vue can be used, or you can import your own .svg files.
iconLeftBooleanfalseDisplays the icon on the left side of the button text if true.
sizeStringbaseSpecifies the button size. Options: sm and base.
spinningBooleanfalseShows a loading spinner in place of the icon when true.
colorStringprimarySets the button's color variant. Accepts predefined values and custom formats (red-soft, primary and custom-*). Learn more about color variants in the next section.
iconOnlyBooleanfalseRenders a button with only an icon. When enabled, an icon should be provided either via the icon prop or icon slot.

Color Variants

The color prop supports both predefined and custom values. Default colors include:

  • primary
  • secondary
  • tertiary
  • light
  • dark
  • red
  • red-soft
  • yellow
  • yellow-soft
  • green
  • green-soft
  • blue
  • blue-soft
  • clear
  • custom-*

Custom Colors

If you don't find a color variant that suits your needs, you can create custom color variants.

Use custom-* color formats to add unique colors directly from your tailwind.config.js file.

Slots

Default Slot

Provides a way to customize the button content with access to the label prop:

vue
<template>
  <ButtonBase label="Click Me" v-slot="{ label }">
    {{ label }} <!-- Access to label prop -->
  </ButtonBase>
</template>

Icon Slot

Customize the icon section with access to the icon prop:

vue
<template>
  <ButtonBase label="Click Me">
    <template #icon="{ icon }">
      <div class="flex items-center gap-1">
        <Component :is="icon" class="size-4" /> <!-- Access to icon prop -->
        <StarIcon class="size-4" />
      </div>
    </template>
  </ButtonBase>
</template>

Icon Placement

The icon slot respects the iconLeft prop, so you can control whether your custom icon content appears on the left or right side of the button text.

Spinner Slot

Customize the spinner with access to the spinning state:

vue
<template label="Save Changes">
  <ButtonBase :spinning="isLoading">
    <template #spinner="{ spinning }">
      <div v-if="spinning" class="custom-loader">
        <CustomSpinnerComponent />
      </div>
    </template>
  </ButtonBase>
</template>

Spinner Visibility

The spinner slot content will only be visible when the spinning prop is true, replacing either the icon (if present) or appearing before the button text.

Customization with Tailwind CSS

To customize the button styles globally, extend your Tailwind configuration. Example:

javascript
// tailwind.config.js
import components from '@robuust-digital/vue-components/tailwind';

export default {
  // ...
  theme: {
    extend: {
      components: (theme) => ({
        button: {
          '--rvc-button-height': theme('height.9'),
          '--rvc-button-border-width': theme('borderWidth.DEFAULT'),
          '--rvc-button-padding-x': theme('padding.3'),
          '--rvc-button-gap': theme('gap[1.5]'),
          '--rvc-button-border-radius': theme('borderRadius.md'),
          '--rvc-button-font-weight': theme('fontWeight.semibold'),
          '--rvc-button-font-size': theme('fontSize.base.0'),
          '--rvc-button-transition-property': theme('transitionProperty.colors'),
          '--rvc-button-transition-duration': theme('transitionDuration.DEFAULT'),
          '--rvc-button-transition-timing-function': theme('transitionTimingFunction.DEFAULT'),
          '--rvc-button-icon-size': theme('width.5'),
          '--rvc-button-icon-loading-size': theme('width.4'),
          '--rvc-button-icon-loading-animation': theme('animation.spin'),
          '--rvc-button-box-shadow': 'none',
          '--rvc-button-bg-color': '#d9ff00',
          '--rvc-button-color': '#000',
          '--rvc-button-bg-color-hover': '#c1e012',
          '--rvc-button-color-hover': '#000',
          '--rvc-button-border-color': theme('colors.transparent'),
          '--rvc-button-border-color-hover': theme('colors.transparent'),
          '--rvc-button-icon-color': theme('colors.slate.500'),
          '--rvc-button-icon-color-hover': theme('colors.slate.950'),
        },
      }),
    },
  },
  plugins: [components],
};

Full Tailwind CSS button component configuration options can be found here.

Sizes

The ButtonBase component supports different sizes through the size prop:

Base size (default)

Show code
vue
<ButtonBase color="primary" label="Base Button" />

Small size

Show code
vue
<ButtonBase color="primary" size="sm" label="Small Button" />

Examples

Basic Colors

Show code
vue
<ButtonBase color="primary" label="Primary" />
<ButtonBase color="secondary" label="Secondary" />
<ButtonBase color="tertiary" label="Tertiary" />
<ButtonBase color="light" label="Light" />
<ButtonBase color="dark" label="Dark" />

System Colors

Show code
vue
<ButtonBase color="green" label="Green" />
<ButtonBase color="red" label="Red" />
<ButtonBase color="yellow" label="Yellow" />
<ButtonBase color="blue" label="Blue" />

Soft System Colors

Show code
vue
<ButtonBase color="green-soft" label="Green Soft" />
<ButtonBase color="red-soft" label="Red Soft" />
<ButtonBase color="yellow-soft" label="Yellow Soft" />
<ButtonBase color="blue-soft" label="Blue Soft" />

Clear Button

Show code
vue
<ButtonBase color="clear" label="Clear Button" />
<ButtonBase color="clear" label="Clear Button with Icon" icon-left :icon="BeakerIcon" />
<ButtonBase color="clear" icon-only label="Clear Button with Icon" :icon="BeakerIcon" />

Button with Icon

Show code
vue
<ButtonBase label="Button Icon" :icon="BeakerIcon" />
<ButtonBase label="Delete" color="red-soft" :icon="TrashIcon" icon-left />

Loading Button

Use spinning to show a loading spinner:

Show code
vue
<ButtonBase color="tertiary" label="Loading Button" :icon="BeakerIcon" spinning />
<ButtonBase color="dark" size="sm" label="Loading Button" :icon="BeakerIcon" spinning />

Custom Button

Show code
vue
<ButtonBase color="custom-foo" label="Custom Button" />

javascript
export default {
  // tailwind.config.js ...
  custom: {
    foo: {
      '--rvc-button-border-radius': theme('borderRadius.full'),
      '--rvc-button-bg-color': theme('colors.pink.200'),
      '--rvc-button-color': theme('colors.pink.800'),
      '--rvc-button-bg-color-hover': theme('colors.pink.300'),
      '--rvc-button-color-hover': theme('colors.pink.900'),
      '--rvc-button-icon-color': theme('colors.pink.700'),
      '--rvc-button-icon-color-hover': theme('colors.red.500'),
    },
  },
  // ...
}

Icon Only Button

Show code
vue
<ButtonBase label="Icon Only Button" color="green-soft" :icon="BeakerIcon" icon-only />

Tailwind Config

Full Tailwind CSS button component configuration options can be found below:

javascript
// tailwind.config.js
import components from '@robuust-digital/vue-components/tailwind';

export default {
  // ...
  theme: {
    extend: {
      components: (theme) => ({
        button: {
          // Available variables you can override:
          '--rvc-button-height': theme('height.9'),
          '--rvc-button-border-width': theme('borderWidth.DEFAULT'),
          '--rvc-button-padding-x': theme('padding.3'),
          '--rvc-button-gap': theme('gap[1.5]'),
          '--rvc-button-border-radius': theme('borderRadius.md'),
          '--rvc-button-font-weight': theme('fontWeight.semibold'),
          '--rvc-button-font-size': theme('fontSize.base.0'),
          '--rvc-button-transition-property': theme('transitionProperty.colors'),
          '--rvc-button-transition-duration': theme('transitionDuration.DEFAULT'),
          '--rvc-button-transition-timing-function': theme('transitionTimingFunction.DEFAULT'),
          '--rvc-button-icon-size': theme('width.5'),
          '--rvc-button-icon-loading-size': theme('width.4'),
          '--rvc-button-icon-loading-animation': theme('animation.spin'),
          '--rvc-button-box-shadow': 'none',

          // Color variables you can override:
          '--rvc-button-bg-color': '#d9ff00',
          '--rvc-button-color': '#000',
          '--rvc-button-bg-color-hover': '#caf400',
          '--rvc-button-color-hover': '#000',
          '--rvc-button-border-color': theme('colors.transparent'),
          '--rvc-button-border-color-hover': theme('colors.transparent'),
          '--rvc-button-icon-color': theme('colors.slate.500'),
          '--rvc-button-icon-color-hover': theme('colors.slate.950'),

          // Color variants
          secondary: {
            '--rvc-button-bg-color': '#4f46e5',
            '--rvc-button-color': '#fff',
            '--rvc-button-bg-color-hover': '#6366f1',
            '--rvc-button-color-hover': '#fff',
            '--rvc-button-icon-color': theme('colors.slate.300'),
            '--rvc-button-icon-color-hover': '#fff',
          },

          tertiary: {
            '--rvc-button-bg-color': '#475569',
            '--rvc-button-color': '#fff',
            '--rvc-button-bg-color-hover': '#64748b',
            '--rvc-button-color-hover': '#fff',
            '--rvc-button-icon-color': theme('colors.slate.300'),
            '--rvc-button-icon-color-hover': '#fff',
          },

          light: {
            '--rvc-button-bg-color': theme('colors.white'),
            '--rvc-button-color': theme('colors.black'),
            '--rvc-button-bg-color-hover': theme('colors.slate.50'),
            '--rvc-button-color-hover': theme('colors.black'),
            '--rvc-button-border-color': theme('colors.slate.200'),
            '--rvc-button-border-color-hover': theme('colors.slate.200'),
          },

          dark: {
            '--rvc-button-bg-color': theme('colors.slate.950'),
            '--rvc-button-color': theme('colors.white'),
            '--rvc-button-bg-color-hover': theme('colors.slate.800'),
            '--rvc-button-color-hover': theme('colors.white'),
            '--rvc-button-icon-color': theme('colors.slate.300'),
            '--rvc-button-icon-color-hover': theme('colors.white'),
          },

          red: {
            '--rvc-button-bg-color': theme('colors.red.600'),
            '--rvc-button-color': theme('colors.white'),
            '--rvc-button-bg-color-hover': theme('colors.red.500'),
            '--rvc-button-color-hover': theme('colors.white'),
            '--rvc-button-icon-color': theme('colors.slate.300'),
            '--rvc-button-icon-color-hover': theme('colors.white'),
          },

          'red-soft': {
            '--rvc-button-bg-color': theme('colors.red.50'),
            '--rvc-button-color': theme('colors.red.700'),
            '--rvc-button-bg-color-hover': theme('colors.red.200'),
            '--rvc-button-color-hover': theme('colors.red.700'),
            '--rvc-button-border-color': theme('colors.red.200'),
            '--rvc-button-border-color-hover': theme('colors.red.200'),
            '--rvc-button-icon-color': theme('colors.red.500'),
            '--rvc-button-icon-color-hover': theme('colors.red.700'),
          },

          yellow: {
            '--rvc-button-bg-color': theme('colors.yellow.300'),
            '--rvc-button-color': theme('colors.slate.950'),
            '--rvc-button-bg-color-hover': theme('colors.yellow.400'),
            '--rvc-button-color-hover': theme('colors.slate.950'),
          },

          'yellow-soft': {
            '--rvc-button-bg-color': theme('colors.yellow.50'),
            '--rvc-button-color': theme('colors.yellow.700'),
            '--rvc-button-bg-color-hover': theme('colors.yellow.200'),
            '--rvc-button-color-hover': theme('colors.yellow.700'),
            '--rvc-button-border-color': theme('colors.yellow.200'),
            '--rvc-button-border-color-hover': theme('colors.yellow.200'),
            '--rvc-button-icon-color': theme('colors.yellow.500'),
            '--rvc-button-icon-color-hover': theme('colors.yellow.700'),
          },

          green: {
            '--rvc-button-bg-color': theme('colors.green.600'),
            '--rvc-button-color': theme('colors.white'),
            '--rvc-button-bg-color-hover': theme('colors.green.500'),
            '--rvc-button-color-hover': theme('colors.white'),
            '--rvc-button-icon-color': theme('colors.slate.300'),
            '--rvc-button-icon-color-hover': theme('colors.white'),
          },

          'green-soft': {
            '--rvc-button-bg-color': theme('colors.green.50'),
            '--rvc-button-color': theme('colors.green.700'),
            '--rvc-button-bg-color-hover': theme('colors.green.200'),
            '--rvc-button-color-hover': theme('colors.green.700'),
            '--rvc-button-border-color': theme('colors.green.200'),
            '--rvc-button-border-color-hover': theme('colors.green.200'),
            '--rvc-button-icon-color': theme('colors.green.500'),
            '--rvc-button-icon-color-hover': theme('colors.green.700'),
          },

          blue: {
            '--rvc-button-bg-color': theme('colors.blue.600'),
            '--rvc-button-color': theme('colors.white'),
            '--rvc-button-bg-color-hover': theme('colors.blue.500'),
            '--rvc-button-color-hover': theme('colors.white'),
            '--rvc-button-icon-color': theme('colors.slate.300'),
            '--rvc-button-icon-color-hover': theme('colors.white'),
          },

          'blue-soft': {
            '--rvc-button-bg-color': theme('colors.blue.50'),
            '--rvc-button-color': theme('colors.blue.700'),
            '--rvc-button-bg-color-hover': theme('colors.blue.200'),
            '--rvc-button-color-hover': theme('colors.blue.700'),
            '--rvc-button-border-color': theme('colors.blue.200'),
            '--rvc-button-border-color-hover': theme('colors.blue.200'),
            '--rvc-button-icon-color': theme('colors.blue.500'),
            '--rvc-button-icon-color-hover': theme('colors.blue.700'),
          },

          clear: {
            '--rvc-button-bg-color': theme('colors.transparent'),
            '--rvc-button-color': theme('colors.indigo.600'),
            '--rvc-button-bg-color-hover': theme('colors.transparent'),
            '--rvc-button-color-hover': theme('colors.indigo.700'),
            '--rvc-button-icon-color': theme('colors.indigo.600'),
            '--rvc-button-icon-color-hover': theme('colors.indigo.700'),
          },

          // Size variants
          sm: {
            '--rvc-button-height': '1.875rem',
            '--rvc-button-padding-x': theme('padding[2.5]'),
            '--rvc-button-font-size': theme('fontSize.sm.0'),
            '--rvc-button-icon-size': theme('width.4'),
            '--rvc-button-icon-loading-size': theme('width.3'),
          },

          // You can also apply additional styles
          '@apply whitespace-nowrap': {},

          //  or
          display: 'none',
        },
      }),
    },
  },
  plugins: [components],
};