Button

Button is an extension to standard input element with icons and theming.


npx volt-vue add Button SecondaryButton ContrastButton DangerButton


import Button from '@/volt/Button.vue';
import SecondaryButton from '@/volt/SecondaryButton.vue';
import ContrastButton from '@/volt/ContrastButton.vue';
import DangerButton from '@/volt/DangerButton.vue';

Text to display on a button is defined with the label property.


<template>
    <div class="card flex justify-center">
        <Button label="Submit" />
    </div>
</template>

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

Icon of a button is specified with icon property and position is configured using iconPos attribute.


<template>
    <div class="card flex flex-col items-center gap-4">
        <div class="flex flex-wrap gap-4 justify-center">
            <Button icon="pi pi-home" aria-label="Save" />
            <Button label="Profile" icon="pi pi-user" />
            <Button label="Save" icon="pi pi-check" iconPos="right" />
        </div>
        <div class="flex flex-wrap gap-4 justify-center">
            <Button label="Search" icon="pi pi-search" iconPos="top" />
            <Button label="Update" icon="pi pi-refresh" iconPos="bottom" />
        </div>
    </div>
</template>

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

Busy state is controlled with the loading property.


<template>
    <div class="card flex justify-center">
        <Button type="button" label="Search" icon="pi pi-check" :loading="loading" @click="load" />
    </div>
</template>

<script setup lang="ts">
import Button from '@/volt/Button.vue';
import { ref } from 'vue';

const loading = ref(false);

const load = () => {
    loading.value = true;
    setTimeout(() => {
        loading.value = false;
    }, 2000);
};
</script>

An example of a router link styled as a Volt button.


<template>
    <div class="card flex justify-center">
        <RouterLink
            to="/"
            class="bg-primary hover:bg-primary-emphasis active:bg-primary-emphasis-alt text-primary-contrast px-3 py-2 gap-2 rounded-md font-medium focus-visible:outline focus-visible:outline-offset-2 focus-visible:outline-primary"
            >Router</RouterLink
        >
    </div>
</template>

<script setup lang="ts">
</script>

Secondary, Contrast and Danger buttons are available as separate components for severity alternatives.


<template>
    <div class="card flex justify-center flex-wrap gap-4">
        <Button label="Primary" />
        <SecondaryButton label="Secondary" />
        <ContrastButton label="Contrast" />
        <DangerButton label="Danger" />
    </div>
</template>

<script setup lang="ts">
import Button from '@/volt/Button.vue';
import ContrastButton from '@/volt/ContrastButton.vue';
import DangerButton from '@/volt/DangerButton.vue';
import SecondaryButton from '@/volt/SecondaryButton.vue';
</script>

When disabled is present, the element cannot be used.


<template>
    <div class="card flex justify-center">
        <Button label="Submit" disabled />
    </div>
</template>

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

Raised buttons display a shadow to indicate elevation.


<template>
    <div class="card flex justify-center flex-wrap gap-4">
        <Button label="Primary" raised />
        <SecondaryButton label="Secondary" raised />
        <ContrastButton label="Contrast" raised />
        <DangerButton label="Danger" raised />
    </div>
</template>

<script setup lang="ts">
import Button from '@/volt/Button.vue';
import ContrastButton from '@/volt/ContrastButton.vue';
import DangerButton from '@/volt/DangerButton.vue';
import SecondaryButton from '@/volt/SecondaryButton.vue';
</script>

Rounded buttons have a circular border radius.


<template>
    <div class="card flex justify-center flex-wrap gap-4">
        <Button label="Primary" rounded />
        <SecondaryButton label="Secondary" rounded />
        <ContrastButton label="Contrast" rounded />
        <DangerButton label="Danger" rounded />
    </div>
</template>

<script setup lang="ts">
import Button from '@/volt/Button.vue';
import ContrastButton from '@/volt/ContrastButton.vue';
import DangerButton from '@/volt/DangerButton.vue';
import SecondaryButton from '@/volt/SecondaryButton.vue';
</script>

Text buttons are displayed as textual elements.


<template>
    <div class="card flex justify-center flex-wrap gap-4">
        <Button label="Primary" variant="text" />
        <SecondaryButton label="Secondary" variant="text" />
        <ContrastButton label="Contrast" variant="text" />
        <DangerButton label="Danger" variant="text" />
    </div>
</template>

<script setup lang="ts">
import Button from '@/volt/Button.vue';
import ContrastButton from '@/volt/ContrastButton.vue';
import DangerButton from '@/volt/DangerButton.vue';
import SecondaryButton from '@/volt/SecondaryButton.vue';
</script>

Text buttons can be displayed elevated with the raised option.


<template>
   <div class="card flex justify-center flex-wrap gap-4">
        <Button label="Primary" variant="text" raised />
        <SecondaryButton label="Secondary" variant="text" raised />
        <ContrastButton label="Contrast" variant="text" raised />
        <DangerButton label="Danger" variant="text" raised />
    </div>
</template>

<script setup lang="ts">
import Button from '@/volt/Button.vue';
import ContrastButton from '@/volt/ContrastButton.vue';
import DangerButton from '@/volt/DangerButton.vue';
import SecondaryButton from '@/volt/SecondaryButton.vue';
</script>

Outlined buttons display a border without a transparent background.


<template>
    <div class="card flex justify-center flex-wrap gap-4">
        <Button label="Primary" variant="outlined" />
        <SecondaryButton label="Secondary" variant="outlined" />
        <ContrastButton label="Contrast" variant="outlined" />
        <DangerButton label="Danger" variant="outlined" />
    </div>
</template>

<script setup lang="ts">
import Button from '@/volt/Button.vue';
import ContrastButton from '@/volt/ContrastButton.vue';
import DangerButton from '@/volt/DangerButton.vue';
import SecondaryButton from '@/volt/SecondaryButton.vue';
</script>

Buttons can have icons without labels.


<template>
    <div class="card">
        <div class="flex justify-center mb-8">
            <SelectButton v-model="size" :options="sizeOptions" optionLabel="label" optionValue="value" dataKey="label" />
        </div>

        <div class="flex flex-wrap justify-center gap-4 mb-6">
            <Button icon="pi pi-check" aria-label="Filter" :size="size" />
            <SecondaryButton icon="pi pi-bookmark" severity="secondary" aria-label="Bookmark" :size="size" />
            <ContrastButton icon="pi pi-search" severity="success" aria-label="Search" :size="size" />
            <DangerButton icon="pi pi-times" severity="info" aria-label="User" :size="size" />
        </div>

        <div class="flex flex-wrap justify-center gap-4 mb-6">
            <Button icon="pi pi-check" aria-label="Filter" :size="size" rounded />
            <SecondaryButton icon="pi pi-bookmark" severity="secondary" aria-label="Bookmark" :size="size" rounded />
            <ContrastButton icon="pi pi-search" severity="success" aria-label="Search" :size="size" rounded />
            <DangerButton icon="pi pi-times" severity="info" aria-label="User" :size="size" rounded />
        </div>

        <div class="flex flex-wrap justify-center gap-4 mb-6">
            <Button icon="pi pi-check" aria-label="Filter" :size="size" variant="outlined" rounded />
            <SecondaryButton icon="pi pi-bookmark" severity="secondary" aria-label="Bookmark" :size="size" variant="outlined" rounded />
            <ContrastButton icon="pi pi-search" severity="success" aria-label="Search" :size="size" variant="outlined" rounded />
            <DangerButton icon="pi pi-times" severity="info" aria-label="User" :size="size" variant="outlined" rounded />
        </div>

        <div class="flex flex-wrap justify-center gap-4 mb-6">
            <Button icon="pi pi-check" aria-label="Filter" :size="size" variant="text" rounded />
            <SecondaryButton icon="pi pi-bookmark" severity="secondary" aria-label="Bookmark" :size="size" variant="text" rounded />
            <ContrastButton icon="pi pi-search" severity="success" aria-label="Search" :size="size" variant="text" rounded />
            <DangerButton icon="pi pi-times" severity="info" aria-label="User" :size="size" variant="text" rounded />
        </div>

        <div class="flex flex-wrap justify-center gap-4 mb-6">
            <Button icon="pi pi-check" aria-label="Filter" :size="size" variant="text" raised rounded />
            <SecondaryButton icon="pi pi-bookmark" severity="secondary" aria-label="Bookmark" :size="size" variant="text" raised rounded />
            <ContrastButton icon="pi pi-search" severity="success" aria-label="Search" :size="size" variant="text" raised rounded />
            <DangerButton icon="pi pi-times" severity="info" aria-label="User" :size="size" variant="text" raised rounded />
        </div>
    </div>
</template>

<script setup lang="ts">
import Button from '@/volt/Button.vue';
import ContrastButton from '@/volt/ContrastButton.vue';
import DangerButton from '@/volt/DangerButton.vue';
import SecondaryButton from '@/volt/SecondaryButton.vue';
import SelectButton from '@/volt/SelectButton.vue';
import { ref } from 'vue';

const size = ref('normal');
const sizeOptions = ref([
    { label: 'Small', value: 'small' },
    { label: 'Normal', value: 'normal' },
    { label: 'Large', value: 'large' }
]);
</script>

Buttons have built-in badge support with the badge property.


<template>
    <div class="card flex justify-center flex-wrap gap-4">
        <Button type="button" label="Emails" badge="2" />
    </div>
</template>

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

Multiple buttons are grouped when wrapped inside an element with ButtonGroup component.


<template>
    <div class="card flex flex-col items-center gap-6">
        <ButtonGroup>
            <Button label="Save" icon="pi pi-check" />
            <Button label="Delete" icon="pi pi-trash" />
            <Button label="Cancel" icon="pi pi-times" />
        </ButtonGroup>

        <ButtonGroup>
            <Button label="Save" icon="pi pi-check" variant="outlined" />
            <Button label="Delete" icon="pi pi-trash" variant="outlined" />
            <Button label="Cancel" icon="pi pi-times" variant="outlined" />
        </ButtonGroup>
    </div>
</template>

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

Button provides small and large sizes as alternatives to the base.


<template>
    <div class="card flex flex-wrap items-center justify-center gap-4">
        <Button label="Small" icon="pi pi-check" size="small" />
        <Button label="Normal" icon="pi pi-check" />
        <Button label="Large" icon="pi pi-check" size="large" />
    </div>
</template>

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

Custom content inside a button is defined as children.


<template>
    <div class="card flex justify-center">
        <Button variant="outlined" class="!border-2">
            <svg width="35" height="40" viewBox="0 0 35 40" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path
                    d="M25.87 18.05L23.16 17.45L25.27 20.46V29.78L32.49 23.76V13.53L29.18 14.73L25.87 18.04V18.05ZM25.27 35.49L29.18 31.58V27.67L25.27 30.98V35.49ZM20.16 17.14H20.03H20.17H20.16ZM30.1 5.19L34.89 4.81L33.08 12.33L24.1 15.67L30.08 5.2L30.1 5.19ZM5.72 14.74L2.41 13.54V23.77L9.63 29.79V20.47L11.74 17.46L9.03 18.06L5.72 14.75V14.74ZM9.63 30.98L5.72 27.67V31.58L9.63 35.49V30.98ZM4.8 5.2L10.78 15.67L1.81 12.33L0 4.81L4.79 5.19L4.8 5.2ZM24.37 21.05V34.59L22.56 37.29L20.46 39.4H14.44L12.34 37.29L10.53 34.59V21.05L12.42 18.23L17.45 26.8L22.48 18.23L24.37 21.05ZM22.85 0L22.57 0.69L17.45 13.08L12.33 0.69L12.05 0H22.85Z"
                    fill="var(--p-primary-color)"
                />
                <path
                    d="M30.69 4.21L24.37 4.81L22.57 0.69L22.86 0H26.48L30.69 4.21ZM23.75 5.67L22.66 3.08L18.05 14.24V17.14H19.7H20.03H20.16H20.2L24.1 15.7L30.11 5.19L23.75 5.67ZM4.21002 4.21L10.53 4.81L12.33 0.69L12.05 0H8.43002L4.22002 4.21H4.21002ZM21.9 17.4L20.6 18.2H14.3L13 17.4L12.4 18.2L12.42 18.23L17.45 26.8L22.48 18.23L22.5 18.2L21.9 17.4ZM4.79002 5.19L10.8 15.7L14.7 17.14H14.74H15.2H16.85V14.24L12.24 3.09L11.15 5.68L4.79002 5.2V5.19Z"
                    fill="var(--p-text-color)"
                />
            </svg>
        </Button>
    </div>
</template>

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

Headless mode is enabled by adding the asChild property and defining your own UI element with the available bindings.


<template>
    <div class="card flex justify-center">
        <Button v-slot="slotProps" asChild>
            <button
                v-bind="slotProps.a11yAttrs"
                class="rounded-lg bg-gradient-to-br from-primary-400 to-primary-700 active:from-primary-700 active:to-primary-900 text-white border-none px-6 py-3 font-bold hover:ring-2 cursor-pointer ring-offset-2 ring-offset-surface-0 dark:ring-offset-surface-900 ring-primary transition-all"
            >
                SIGN UP
            </button>
        </Button>
    </div>
</template>

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

Key distinctions to notice between the Volt Button and the PrimeVue Button.

  • The severity prop is not utilized in Volt, Use the semantic buttons such as ContrastButton instead of the contrast severity. Volt provides four common severities compared to the wider range of PrimeVue.
  • The badgeSeverity is not used, the fixed style is provided by the pcBadge section instead.