Skip to content

Button

Buttons trigger actions and communicate what will happen when pressed.

ts
import { CoarButton } from '@cocoar/vue-ui';

Variants

Choose the appropriate variant based on the action's importance and context.

vue
<template>
  <div style="display: flex; flex-wrap: wrap; gap: 8px; align-items: center;">
    <CoarButton variant="primary">Primary</CoarButton>
    <CoarButton variant="secondary">Secondary</CoarButton>
    <CoarButton variant="tertiary">Tertiary</CoarButton>
    <CoarButton variant="danger">Danger</CoarButton>
    <CoarButton variant="ghost">Ghost</CoarButton>
  </div>
</template>

<script setup lang="ts">
import { CoarButton } from '@cocoar/vue-ui';
</script>

When to use each variant:

  • Primary — Main call-to-action. Use sparingly, typically once per view.
  • Secondary — Alternative actions. Pairs well with primary buttons.
  • Tertiary — Low-emphasis actions with brand color hint.
  • Danger — Destructive actions like delete or remove.
  • Ghost — Minimal emphasis, often for cancel or dismiss.

Sizes

Four sizes to fit different contexts and layouts.

xs 27px | s 32px | m 40px | l 48px

vue
<template>
  <div style="display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end;">
    <CoarButton size="xs">Extra Small</CoarButton>
    <CoarButton size="s">Small</CoarButton>
    <CoarButton size="m">Medium</CoarButton>
    <CoarButton size="l">Large</CoarButton>
  </div>
  <p style="margin-top: 8px; font-size: 13px; color: #64748b;">
    <code>xs</code> 27px | <code>s</code> 32px | <code>m</code> 40px | <code>l</code> 48px
  </p>
</template>

<script setup lang="ts">
import { CoarButton } from '@cocoar/vue-ui';
</script>

Icons

Add icons before or after the label to enhance meaning.

vue
<template>
  <div style="display: flex; flex-wrap: wrap; gap: 8px; align-items: center;">
    <CoarButton icon-start="plus">Add Item</CoarButton>
    <CoarButton variant="secondary" icon-end="chevron-right">Next</CoarButton>
    <CoarButton variant="tertiary" icon-start="clipboard">Download</CoarButton>
    <CoarButton variant="danger" icon-start="trash-2">Delete</CoarButton>
  </div>
</template>

<script setup lang="ts">
import { CoarButton } from '@cocoar/vue-ui';
</script>

Loading State

Show a spinner while an async action is in progress. Click to test.

With start icon:
Icon end:
vue
<template>
  <div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
    <div style="display: flex; align-items: center; gap: 8px;">
      <span style="font-size: 13px; color: #64748b; min-width: 100px;">With start icon:</span>
      <CoarButton icon-start="check" :loading="isLoading" @click="simulateLoading">Save Changes</CoarButton>
    </div>
    <div style="display: flex; align-items: center; gap: 8px;">
      <span style="font-size: 13px; color: #64748b; min-width: 100px;">Icon end:</span>
      <CoarButton icon-end="chevron-right" :loading="isLoadingEnd" @click="simulateLoadingEnd">Continue</CoarButton>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { CoarButton } from '@cocoar/vue-ui';

const isLoading = ref(false);
const isLoadingEnd = ref(false);

function simulateLoading() {
  isLoading.value = true;
  setTimeout(() => { isLoading.value = false; }, 2000);
}

function simulateLoadingEnd() {
  isLoadingEnd.value = true;
  setTimeout(() => { isLoadingEnd.value = false; }, 2000);
}
</script>

Disabled State

Disable buttons when actions are not available.

vue
<template>
  <div style="display: flex; flex-wrap: wrap; gap: 8px; align-items: center;">
    <CoarButton :disabled="true">Primary</CoarButton>
    <CoarButton variant="secondary" :disabled="true">Secondary</CoarButton>
    <CoarButton variant="tertiary" :disabled="true">Tertiary</CoarButton>
    <CoarButton variant="danger" :disabled="true">Danger</CoarButton>
    <CoarButton variant="ghost" :disabled="true">Ghost</CoarButton>
  </div>
</template>

<script setup lang="ts">
import { CoarButton } from '@cocoar/vue-ui';
</script>

Full Width

Buttons can expand to fill their container.

vue
<template>
  <div style="max-width: 360px; display: flex; flex-direction: column; gap: 8px;">
    <CoarButton :full-width="true">Full Width Primary</CoarButton>
    <CoarButton variant="secondary" :full-width="true">Full Width Secondary</CoarButton>
  </div>
</template>

<script setup lang="ts">
import { CoarButton } from '@cocoar/vue-ui';
</script>

Accessibility

Keyboard Navigation

KeyAction
TabMove focus to button
Shift + TabMove focus backward
EnterActivate button
SpaceActivate button

INFO

Disabled and loading buttons cannot be activated via keyboard.

Screen Reader Support

  • Button text or aria-label announces on focus
  • Disabled state properly communicated
  • Loading state indicates button is busy
  • Icon-only buttons should include aria-label
  • type attribute ensures correct form behavior

API

Props

PropTypeDefaultDescription
variant'primary' | 'secondary' | 'tertiary' | 'danger' | 'ghost''primary'Button style variant
size'xs' | 's' | 'm' | 'l''m'Button size
iconStartstringundefinedIcon name before label
iconEndstringundefinedIcon name after label
loadingbooleanfalseShow loading spinner
disabledbooleanfalseDisable the button
fullWidthbooleanfalseExpand to fill container
type'button' | 'submit' | 'reset''button'HTML button type

Events

EventPayloadDescription
clickMouseEventEmitted when clicked (not when disabled/loading)

i18n Keys

These keys can be translated via @cocoar/vue-localization.

KeyDefault (English)Used as
coar.ui.button.loading'Loading'Screen reader announcement when loading is true

Released under the Apache-2.0 License.