QButton component

QButton is used to trigger actions or to display links. It includes a variety of classes to customize appearance.

Styling

Available variants

To be assigned with variant binding

  • primary: Uses CSS color --accent-color-primary
  • secondary: Uses CSS color --accent-color-secondary
  • info: Uses CSS color --color-info
  • success: Uses CSS color --color-success
  • warning: Uses CSS color --color-warning
  • error: Uses CSS color --color-error
  • dark: Uses CSS color --black-color
  • bright: Uses CSS color --white-color

Available coloring

Background coloring

To be assigned with bgColor binding

  • primary Uses the --accent-color-primary CSS variable
  • secondary Uses the --accent-color-secondary CSS variable

Text coloring

To be assigned with textColor binding

If undefined, the text color will depend on the color theme

  • bright Uses the --accent-color-primary CSS variable
  • dark Uses the --accent-color-secondary CSS variable

Requirements

TypePath / VersionPurposeOptional
Styles../../assets/main.cssCSS VariablesNo
Functions../../use/uuidAssign ids to itemsNo

Usage

Import the following component/s:

import QButton from '../../components/Form/QButton.vue'

Basic usage

By default, the button has the defined primary color set. The text color switches with the currently active theme, the label & the loader are toggled based on whether loading is set to true or false

Loader idle

Example

<template>
  <q-button label="Submit form" :loading="isLoading" @click="onButtonClick" />
  <p v-if="isLoading && loadingCounter > 1">Done in {{loadingCounter}} seconds ...</p>
  <p v-else-if="isLoading && loadingCounter === 1">Almost there ...</p>
  <p v-else>Loader idle</p>
</template>

<script setup>
import { ref } from 'vue';

let isLoading = ref(false);
let loadingCounter = ref(3)

const onButtonClick = () => {
  isLoading.value = true
  loadingCounter.value = 3;
  const countdown = setInterval(() => {
    loadingCounter.value -= 1;
    if(loadingCounter.value <= 0) {
      isLoading.value = false
      return clearInterval(countdown)
    }
  }, 1000)
}
</script>

Variants & sizing

Coloring and sizing can be customized using variant and size bindings.

Small sizing | Added by size="small"

Example

<template>
  <q-button variant="info" size="small" label="Info" :loading="isLoading" @click="onButtonClick" />
</template>

Default sizing | Default setting

Example

<template>
  <q-button variant="info" size="medium" label="Info" :loading="isLoading" @click="onButtonClick" />
</template>

Large sizing

Example

<template>
  <q-button variant="info" size="large" label="Info" :loading="isLoading" @click="onButtonClick" />
</template>

XL sizing

Example

<template>
  <q-button variant="info" size="xlarge" label="Info" :loading="isLoading" @click="onButtonClick" />
</template>

Loader idle

Full width | Added by fluid binding:

Example

<template>
  <q-button variant="info" fluid label="Info" :loading="isLoading" @click="onButtonClick" />
</template>

Wave effect

Once registered, you can use the v-ripple - directive on these buttons, making them resemble their material design counterparts. Read more about ripple effects here

Full component's code

QButton

<template>
  <button
    class="q-button-base"
    :id="id"
    :disabled="loading"
    :aria-disabled="loading"
    :aria-busy="loading"
    :class="{
      'q-button-u-full-width': fluid === true,
      'q-button-u-disabled': loading,
      'q-button-v-primary': variant === 'primary',
      'q-button-v-secondary': variant === 'secondary',
      'q-button-v-info': variant === 'info',
      'q-button-v-success': variant === 'success',
      'q-button-v-warning': variant === 'warning',
      'q-button-v-error': variant === 'error',
      'q-button-v-link': variant === 'link',
      'q-button-s-small': size === 'small',
      'q-button-s-medium': size === 'medium',
      'q-button-s-large': size === 'large',
      'q-button-s-xlarge': size === 'xlarge',
    }"
  >
    <i v-if="loading">
      <div class="loader"></div>
    </i>
    <span v-else>{{ label }}</span>
  </button>
</template>

<script setup>
import uuid from "../../use/uuid";
const id = uuid();

defineProps({
  loading: {
    type: Boolean,
    default: false,
  },
  label: {
    type: String,
    required: true,
    default: "",
  },
  fluid: {
    type: Boolean,
    default: false,
  },
  variant: {
    type: String,
    required: false,
    default: "primary",
    validator(value) {
      const isPrimary = value === "primary";
      const isSecondary = value === "secondary";
      const isInfo = value === "info";
      const isSuccess = value === "success";
      const isWarning = value === "warning";
      const isError = value === "error";
      const isDark = value === "dark";
      const isBright = value === "bright";
      return (
        isPrimary ||
        isSecondary ||
        isInfo ||
        isSuccess ||
        isWarning ||
        isError ||
        isDark ||
        isBright
      );
    },
  },
  size: {
    type: String,
    default: "medium",
    validator(value) {
      const isSmall = value === "small";
      const isMedium = value === "medium";
      const isLarge = value === "large";
      const isXlarge = value === "xlarge";
      return isSmall || isMedium || isLarge || isXlarge;
    },
  },
});
</script>

<style scoped>
.q-button-base {
  border: none;
  border-radius: var(--gap-xs);
  cursor: pointer;
  margin: var(--gap-sm) var(--gap-sm) var(--gap-sm) 0;
  text-align: center;
  font-weight: 600;
}

/* Util classes for the button */
.q-button-u-fluid {
  width: 100%;
  margin: 0;
}

.q-button-base.q-button-u-full-width {
  display: block;
  width: 100%;
}

.q-button-u-disabled {
  opacity: var(--opacity-disabled);
}

/* Variant classes */
.q-button-v-primary {
  background-color: var(--accent-color-primary);
}

.q-button-v-secondary {
  background-color: var(--accent-color-secondary);
}

.q-button-v-info {
  background-color: var(--color-info);
  color: var(--white-color);
}

.q-button-v-success {
  background-color: var(--color-success);
  color: var(--white-color);
}

.q-button-v-warning {
  background-color: var(--color-warning);
  color: var(--white-color);
}

.q-button-v-error {
  background-color: var(--color-error);
  color: var(--white-color);
}

.q-button-v-primary .loader,
.q-button-v-secondary .loader {
  border-top: 2px solid var(--background-color-tartiary);
}

.q-button-v-info .loader,
.q-button-v-success .loader,
.q-button-v-warning .loader,
.q-button-v-error .loader {
  border-top: 2px solid var(--white-color);
}

.q-button-v-link {
  background-color: transparent !important;
  padding: var(--gap-sm);
  color: var(--accent-color-primary) !important;
  font-size: var(--text-size-sm);
  width: fit-content;
}

.q-button-v-link:hover {
  text-decoration: underline;
}

/* Sizing classes */
.q-button-s-small {
  padding: var(--gap-sm) var(--gap-lg);
  font-size: var(--text-size-xs);
  width: var(--el-size-sm);
}

.q-button-s-small .loader {
  width: var(--text-size-xs);
  height: var(--text-size-xs);
}

.q-button-s-medium {
  padding: var(--gap-sm) var(--gap-xl);
  font-size: var(--text-size-sm);
  width: var(--el-size-md);
}

.q-button-s-medium .loader {
  width: var(--text-size-sm);
  height: var(--text-size-sm);
}

.q-button-s-large {
  padding: var(--gap-md) var(--gap-xxl);
  font-size: var(--text-size-md);
  height: var(--el-size-xxs);
  width: var(--el-size-lg);
}

.q-button-s-large .loader {
  width: var(--text-size-md);
  height: var(--text-size-md);
}

.q-button-s-xlarge {
  padding: var(--gap-md) var(--gap-xxl);
  font-size: var(--text-size-lg);
  height: var(--el-size-xs);
  width: var(--el-size-xl);
}

.q-button-s-xlarge .loader {
  width: var(--text-size-lg);
  height: var(--text-size-lg);
}

/* Text color classes */
.q-button-c-text-bright {
  color: var(--white-color) !important;
}

.q-button-c-text-dark {
  color: var(--black-color) !important;
}

/* Styles for the loader */
.loader {
  display: block;
  margin: auto;
  padding: var(--gap-xs);
  border-radius: 50%;
  border-right: 2px solid transparent;
  animation: fullRotation var(--duration-semi) linear infinite;
}
@keyframes fullRotation {
  to {
    transform: rotate(360deg);
  }
}
</style>