Skip to content

@carrot/design-vue-badges

Vue 3 badge components for the Carrot Design System. Provides label badges, notification counts, and status dots.

Installation

ts
import { CarrotBadge, CarrotBadgeCount, CarrotDot } from "@carrot/design-vue-badges";

Requires the CSS layer from @carrot/design-components-badges.

Components

CarrotBadge

Label badge with variant styling, auto-color generation, and optional dismiss button.

Usage

vue
<!-- Basic -->
<CarrotBadge label="New" />

<!-- Variants -->
<CarrotBadge label="Active" variant="success" />
<CarrotBadge label="Warning" variant="warning-subtle" />
<CarrotBadge label="Outline" variant="outline" />

<!-- Auto-colored genre pills -->
<CarrotBadge label="Electronic" auto-color />
<CarrotBadge label="Jazz" auto-color />
<CarrotBadge label="Hip Hop" auto-color />

<!-- Custom colors -->
<CarrotBadge label="Custom" bg-color="#ff6b6b" />

<!-- Interactive -->
<CarrotBadge label="Click me" interactive @click="handleClick" />

<!-- Dismissible -->
<CarrotBadge label="Removable" dismissible @dismiss="handleRemove" />

<!-- With icons -->
<CarrotBadge label="Tagged">
  <template #left><TagIcon /></template>
</CarrotBadge>

<!-- Sizes -->
<CarrotBadge label="Small" size="sm" />
<CarrotBadge label="Large" size="lg" />

<!-- Shape -->
<CarrotBadge label="Square" :pill="false" square />

Props

PropTypeDefaultDescription
labelstringrequiredBadge text content
variantBadgeVariant"accent-subtle"Visual variant
sizeBadgeSize"md"sm · md · lg
pillbooleantrueFull border radius
squarebooleanfalseSmall border radius
interactivebooleanfalseButton role + hover state
dismissiblebooleanfalseShow remove button, emit dismiss
autoColorbooleanfalseDeterministic color from label text
bgColorstringOverride background color
fgColorstringOverride text color (auto-contrasted from bgColor)

Events

EventPayloadDescription
dismissRemove button clicked

Slots

SlotDescription
leftLeading icon
rightTrailing icon (hidden when dismissible)
dismiss-iconCustom dismiss button content (default: ×)

CarrotBadgeCount

Notification count badge — works standalone (inline) or anchored to a child element.

Usage

vue
<!-- Inline count -->
<CarrotBadgeCount :count="5" />

<!-- Overflow -->
<CarrotBadgeCount :count="150" :max="99" />
<!-- Renders: "99+" -->

<!-- Anchored to an icon/button -->
<CarrotBadgeCount :count="3">
  <BellIcon />
</CarrotBadgeCount>

<!-- Pulse on change -->
<CarrotBadgeCount :count="unreadCount" pulse />

<!-- Show zero -->
<CarrotBadgeCount :count="0" :hide-zero="false" />

<!-- Custom styling -->
<CarrotBadgeCount :count="7" variant="accent" bg-color="#7c5cff" />

Props

PropTypeDefaultDescription
countnumberrequiredThe count to display
maxnumber99Overflow threshold (shows max+)
variantBadgeVariant"danger"Visual variant
sizeBadgeSize"sm"Size preset
hideZerobooleantrueHide badge when count is 0
pulsebooleanfalsePulse animation on count change
bgColorstringOverride background color
fgColorstringOverride text color

Slots

SlotDescription
defaultAnchor element — when provided, count positions as superscript

CarrotDot

Minimal status dot indicator.

Usage

vue
<!-- Basic -->
<CarrotDot />

<!-- Variants -->
<CarrotDot variant="success" aria-label="Online" />
<CarrotDot variant="danger" aria-label="Error" />

<!-- Pulsing (live indicator) -->
<CarrotDot variant="success" pulse aria-label="Live" />

<!-- Custom color -->
<CarrotDot color="#ff6b6b" aria-label="Custom status" />

Props

PropTypeDefaultDescription
variantBadgeVariant"accent"Visual variant
colorstringOverride dot color
pulsebooleanfalsePulsing animation
ariaLabelstringAccessible label (sets role="img", otherwise presentation)

Dependencies

PackagePurpose
@carrot/coreColorFromString for auto-color and contrast
@carrot/design-components-badgesCSS tokens + generated styles

Build

bash
npm run build

Built with Vite + vite-plugin-dts. Outputs ES module with TypeScript declarations.


Usage Guide

Setup

ts
import { CarrotBadge, CarrotBadgeCount, CarrotDot } from "@carrot/design-vue-badges";
import "@carrot/design-components-badges";

CarrotBadge

Status and variant badges

vue
<script setup lang="ts">
import { CarrotBadge } from "@carrot/design-vue-badges";
</script>

<template>
  <CarrotBadge label="Active" variant="success" />
  <CarrotBadge label="Pending" variant="warning-subtle" />
  <CarrotBadge label="Archived" variant="muted" />
  <CarrotBadge label="Error" variant="danger" />
</template>

Auto-colored genre/tag pills

vue
<script setup lang="ts">
import { CarrotBadge } from "@carrot/design-vue-badges";

const genres = ["Electronic", "Jazz", "Hip Hop", "Classical", "Rock"];
</script>

<template>
  <!-- Each badge gets a unique deterministic color derived from its label -->
  <div style="display: flex; gap: 6px; flex-wrap: wrap">
    <CarrotBadge v-for="genre in genres" :key="genre" :label="genre" auto-color />
  </div>
</template>

Dismissible filter chips

vue
<script setup lang="ts">
import { CarrotBadge } from "@carrot/design-vue-badges";
import { ref } from "vue";

const activeFilters = ref(["Electronic", "Live", "2024"]);

function removeFilter(filter: string) {
  activeFilters.value = activeFilters.value.filter((f) => f !== filter);
}
</script>

<template>
  <div style="display: flex; gap: 6px">
    <CarrotBadge
      v-for="filter in activeFilters"
      :key="filter"
      :label="filter"
      dismissible
      @dismiss="removeFilter(filter)"
    />
  </div>
</template>

CarrotBadgeCount

Notification bell with count

vue
<script setup lang="ts">
import { CarrotBadgeCount } from "@carrot/design-vue-badges";
import { ref } from "vue";

const unreadCount = ref(7);
</script>

<template>
  <!-- Count anchors as superscript over the icon -->
  <CarrotBadgeCount :count="unreadCount" pulse>
    <BellIcon />
  </CarrotBadgeCount>

  <!-- Standalone inline count -->
  <span>Notifications</span>
  <CarrotBadgeCount :count="unreadCount" />
</template>

CarrotDot

Live status indicator

vue
<script setup lang="ts">
import { CarrotDot } from "@carrot/design-vue-badges";

defineProps<{ isLive: boolean }>();
</script>

<template>
  <div style="display: flex; align-items: center; gap: 6px">
    <CarrotDot :variant="isLive ? 'success' : 'muted'" :pulse="isLive" :aria-label="isLive ? 'Live' : 'Offline'" />
    <span>{{ isLive ? "Live" : "Offline" }}</span>
  </div>
</template>

Common Patterns

Tag list with overflow

vue
<script setup lang="ts">
import { CarrotBadge } from "@carrot/design-vue-badges";
import { CarrotOverflowRow } from "@carrot/design-vue-core";

const tags = [
  { id: 1, label: "House" },
  { id: 2, label: "Electronic" },
  { id: 3, label: "Minimal" },
  { id: 4, label: "Deep" },
];
</script>

<template>
  <CarrotOverflowRow :items="tags">
    <template #item="{ item }">
      <CarrotBadge :label="item.label" auto-color size="sm" />
    </template>
    <template #overflow="{ hiddenCount }">
      <CarrotBadge :label="`+${hiddenCount}`" variant="muted" size="sm" />
    </template>
  </CarrotOverflowRow>
</template>

Carrot