Appearance
@carrot/design-vue-buttons
Vue 3 button components for the Carrot Design System. Polymorphic rendering, custom color cascading, and full accessibility.
Installation
ts
import { CarrotButton, CarrotButtonGroup } from "@carrot/design-vue-buttons";Requires the CSS layer from @carrot/design-components-buttons.
Components
CarrotButton
Polymorphic button with variant styling, loading state, and custom color support.
Usage
vue
<!-- Basic variants -->
<CarrotButton>Primary</CarrotButton>
<CarrotButton variant="secondary">Secondary</CarrotButton>
<CarrotButton variant="ghost">Ghost</CarrotButton>
<CarrotButton variant="danger">Delete</CarrotButton>
<CarrotButton variant="link">Learn more</CarrotButton>
<!-- Sizes -->
<CarrotButton size="sm">Small</CarrotButton>
<CarrotButton size="md">Medium</CarrotButton>
<CarrotButton size="lg">Large</CarrotButton>
<!-- Shapes -->
<CarrotButton shape="circle" icon aria-label="Play">
<PlayIcon />
</CarrotButton>
<!-- Loading -->
<CarrotButton :loading="isSubmitting">Save</CarrotButton>
<!-- Full width -->
<CarrotButton full>Continue</CarrotButton>
<!-- Icon button (square aspect ratio) -->
<CarrotButton icon aria-label="Settings">
<SettingsIcon />
</CarrotButton>
<!-- Custom color (cascades through all accent-derived tokens) -->
<CarrotButton color="#e91e63">Pink Button</CarrotButton>
<!-- Render as a link -->
<CarrotButton as="a" href="/dashboard">Go to Dashboard</CarrotButton>
<!-- Render as a router-link -->
<CarrotButton :as="RouterLink" :to="{ name: 'settings' }">Settings</CarrotButton>
<!-- Submit button -->
<CarrotButton type="submit">Submit Form</CarrotButton>
<!-- Disabled -->
<CarrotButton disabled>Can't touch this</CarrotButton>Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | ButtonVariant | "primary" | primary · secondary · ghost · danger · link |
size | ButtonSize | "md" | sm · md · lg |
shape | ButtonShape | "default" | default · circle (full radius + hover/active scale) |
loading | boolean | false | Shows spinner, disables interaction |
full | boolean | false | Full width |
icon | boolean | false | Square aspect ratio for icon-only buttons |
disabled | boolean | false | Disabled state |
color | string | — | Custom accent color (cascades via --color-accent) |
as | string | Component | "button" | Polymorphic element/component |
type | "button" | "submit" | "reset" | "button" | Native button type (only for <button>) |
Events
| Event | Payload | Description |
|---|---|---|
click | MouseEvent | Click handler (prevented when disabled/loading) |
Slots
| Slot | Description |
|---|---|
default | Button content (text, icons, etc.) |
Shape: Circle
Circle buttons are commonly used for play controls, FABs, and tile overlays. The circle shape applies border-radius: var(--radius-full) with a subtle hover scale(1.1) and active scale(1.05). Combine with icon for a perfect circle:
vue
<CarrotButton shape="circle" icon variant="primary" aria-label="Play">
<PlayIcon />
</CarrotButton>Custom Color Cascade
The color prop sets --color-accent locally on the button element. Since variant tokens like --btn-primary-bg are defined as var(--color-accent) in the component token layer, they automatically resolve to the custom color — no per-variant overrides needed. Hover and active states are derived using color-mix().
Accessibility
- Native
disabledfor<button>elements,aria-disabledfor others aria-busy="true"during loading statetabindex="-1"on disabled non-button elements- Click prevented when disabled or loading
CarrotButtonGroup
Groups buttons with shared border radius handling and overlap.
Usage
vue
<CarrotButtonGroup label="Actions">
<CarrotButton variant="secondary">Left</CarrotButton>
<CarrotButton variant="secondary">Center</CarrotButton>
<CarrotButton variant="secondary">Right</CarrotButton>
</CarrotButtonGroup>
<!-- As toolbar -->
<CarrotButtonGroup role="toolbar" label="Text formatting">
<CarrotButton icon><BoldIcon /></CarrotButton>
<CarrotButton icon><ItalicIcon /></CarrotButton>
<CarrotButton icon><UnderlineIcon /></CarrotButton>
</CarrotButtonGroup>Props
| Prop | Type | Default | Description |
|---|---|---|---|
role | "group" | "toolbar" | "group" | ARIA role |
label | string | — | Accessible group label |
Dependencies
| Package | Purpose |
|---|---|
@carrot/design-components-buttons | CSS tokens + generated styles |
Build
bash
npm run buildBuilt with Vite + vite-plugin-dts. Outputs ES module with TypeScript declarations.
Usage Guide
Setup
ts
import { CarrotButton, CarrotButtonGroup } from "@carrot/design-vue-buttons";
import "@carrot/design-components-buttons";CarrotButton
Variants and states
vue
<script setup lang="ts">
import { CarrotButton } from "@carrot/design-vue-buttons";
import { ref } from "vue";
const saving = ref(false);
async function save() {
saving.value = true;
await api.save();
saving.value = false;
}
</script>
<template>
<CarrotButton @click="save" :loading="saving">Save</CarrotButton>
<CarrotButton variant="secondary">Cancel</CarrotButton>
<CarrotButton variant="ghost">Reset</CarrotButton>
<CarrotButton variant="danger" @click="deleteItem">Delete</CarrotButton>
</template>Polymorphic — link and router-link
vue
<script setup lang="ts">
import { CarrotButton } from "@carrot/design-vue-buttons";
import { RouterLink } from "vue-router";
</script>
<template>
<!-- Native anchor -->
<CarrotButton as="a" href="https://docs.example.com" variant="link">
Read the docs
</CarrotButton>
<!-- Vue Router link -->
<CarrotButton :as="RouterLink" :to="{ name: 'settings' }" variant="secondary">
Settings
</CarrotButton>
</template>Icon button with circle shape
vue
<script setup lang="ts">
import { CarrotButton } from "@carrot/design-vue-buttons";
</script>
<template>
<!-- Square icon button -->
<CarrotButton icon variant="ghost" aria-label="Edit">
<EditIcon />
</CarrotButton>
<!-- Circular FAB-style -->
<CarrotButton shape="circle" icon variant="primary" size="lg" aria-label="Play">
<PlayIcon />
</CarrotButton>
</template>CarrotButtonGroup
Toolbar
vue
<script setup lang="ts">
import { CarrotButton, CarrotButtonGroup } from "@carrot/design-vue-buttons";
import { ref } from "vue";
const bold = ref(false);
const italic = ref(false);
</script>
<template>
<CarrotButtonGroup role="toolbar" label="Text formatting">
<CarrotButton icon :variant="bold ? 'primary' : 'ghost'" @click="bold = !bold" aria-label="Bold">
<BoldIcon />
</CarrotButton>
<CarrotButton icon :variant="italic ? 'primary' : 'ghost'" @click="italic = !italic" aria-label="Italic">
<ItalicIcon />
</CarrotButton>
</CarrotButtonGroup>
</template>Common Patterns
Submit form button
vue
<script setup lang="ts">
import { CarrotButton } from "@carrot/design-vue-buttons";
defineProps<{ submitting: boolean; valid: boolean }>();
</script>
<template>
<CarrotButton type="submit" full :loading="submitting" :disabled="!valid">
Create Account
</CarrotButton>
</template>Custom brand color
vue
<template>
<!-- The color cascades to all variant tokens on this button -->
<CarrotButton color="#e91e63">Subscribe</CarrotButton>
</template>