Skip to content

Badge

A compact label for counts, statuses, and short metadata. Supports numeric overflow capping, a dot-only indicator, an optional icon slot, and all color themes.

Features

  • 🎨 5 Variants: solid, flat, bordered, outline, frost
  • 🌈 6 Semantic Colors: primary, secondary, info, success, warning, error
  • 📏 3 Sizes: sm, md, lg
  • 🔢 Count Mode: display numeric counts with an optional max cap (e.g. 99+)
  • 🔴 Dot Mode: minimal dot indicator when no label needed- 📌 Overlay Mode: anchor prop pins the badge to a corner of any slotted content- 🔧 Icon Slot: prepend an icon inside the badge

Source Code

View Source Code
ts
import { computed, defineComponent, html } from '@vielzeug/craftit';

import type { ComponentSize, RoundedSize, ThemeColor, VisualVariant } from '../../types';

import { colorThemeMixin, frostVariantMixin, roundedVariantMixin, sizeVariantMixin } from '../../styles';
import componentStyles from './badge.css?inline';

type BadgeVariant = Extract<VisualVariant, 'solid' | 'flat' | 'bordered' | 'outline' | 'frost'>;

/** Badge component properties */
export type BitBadgeProps = {
  /**
   * When set, switches to overlay mode: the host becomes `position:relative`
   * and the badge pins to a corner over the slotted content.
   * Value controls which corner: 'top-end' (default) | 'top-start' | 'bottom-end' | 'bottom-start'
   */
  anchor?: 'top-end' | 'top-start' | 'bottom-end' | 'bottom-start';
  /** Accessible label for assistive technology. Recommended for count-only and dot mode. */
  ariaLabel?: string;
  /** Theme color */
  color?: ThemeColor;
  /** Numeric count to display */
  count?: number;
  /** Render as a small dot with no label */
  dot?: boolean;
  /** Max count — displays "<max>+" when count exceeds this value */
  max?: number;
  /** Border radius override */
  rounded?: RoundedSize;
  /** Badge size */
  size?: ComponentSize;
  /** Visual style variant */
  variant?: BadgeVariant;
};

/**
 * A compact badge/chip for counts, statuses, and labels.
 * Supports numeric counts with overflow, dot mode, and icon slots.
 *
 * @element bit-badge
 *
 * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'
 * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'frost'
 * @attr {string} size - Component size: 'sm' | 'md' | 'lg'
 * @attr {number} count - Numeric count to display
 * @attr {number} max - Max count before showing "<max>+"
 * @attr {boolean} dot - Render as a dot indicator (no text)
 * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'full' etc.
 *
 * @slot - Badge label text
 * @slot icon - Icon displayed before the label
 * @slot target - Element the badge is anchored to (used with the anchor attribute)
 *
 * @cssprop --badge-bg - Badge background color
 * @cssprop --badge-color - Badge text color
 * @cssprop --badge-border-color - Badge border color
 * @cssprop --badge-radius - Border radius
 * @cssprop --badge-font-size - Font size
 * @cssprop --badge-font-weight - Font weight
 * @cssprop --badge-padding-x - Horizontal padding
 * @cssprop --badge-padding-y - Vertical padding
 * @cssprop --badge-gap - Gap between icon and label
 *
 * @example
 * ```html
 * <bit-badge color="primary">New</bit-badge>
 * <bit-badge color="error" count="5"></bit-badge>
 * <bit-badge color="error" count="120" max="99"></bit-badge>
 * <bit-badge color="success" dot></bit-badge>
 * <bit-badge color="warning" variant="flat">Beta</bit-badge>
 * ```
 */
export const BADGE_TAG = defineComponent<BitBadgeProps>({
  props: {
    anchor: { default: undefined },
    ariaLabel: { default: undefined },
    color: { default: undefined },
    count: { default: undefined },
    dot: { default: false },
    max: { default: undefined },
    rounded: { default: undefined },
    size: { default: undefined },
    variant: { default: undefined },
  },
  setup({ props }) {
    const label = computed(() => {
      const count = props.count.value != null ? Number(props.count.value) : undefined;
      const max = props.max.value != null ? Number(props.max.value) : undefined;

      if (count === undefined || Number.isNaN(count)) return undefined;

      if (max !== undefined && !Number.isNaN(max) && count > max) return `${max}+`;

      return String(count);
    });

    return html`<span class="badge" part="badge" aria-label=${() => props.ariaLabel.value}>
        <slot name="icon"></slot>
        <span ?hidden=${() => label.value == null}>${() => label.value}</span>
        <slot ?hidden=${() => label.value != null}></slot>
      </span>
      <slot name="target"></slot>`;
  },
  styles: [
    colorThemeMixin,
    roundedVariantMixin,
    frostVariantMixin('.badge'),
    sizeVariantMixin({
      lg: { '--_padding-x': 'var(--size-2-5)', '--_padding-y': 'var(--size-1)', fontSize: 'var(--text-sm)' },
      md: { '--_padding-x': 'var(--size-2)', '--_padding-y': 'var(--size-0-5)', fontSize: 'var(--text-xs)' },
      sm: { '--_padding-x': 'var(--size-1-5)', '--_padding-y': 'var(--size-px)', fontSize: 'var(--text-xs)' },
    }),
    componentStyles,
  ],
  tag: 'bit-badge',
});

Basic Usage

html
<bit-badge color="primary">New</bit-badge>

<script type="module">
  import '@vielzeug/buildit/badge';
</script>

Visual Options

Variants

The badge comes with five visual variants to match different levels of emphasis.

PreviewCode
RTL

Frost Variant

Modern frost effect with backdrop blur that adapts based on color:

  • Without color: Subtle canvas-based frost overlay
  • With color: Frosted glass effect with colored tint

Best Used With

Frost variant works best when placed over colorful backgrounds or images to showcase the blur and transparency effects.

PreviewCode
RTL

Colors

Six semantic colors for different contexts.

PreviewCode
RTL

Sizes

Three sizes for different contexts.

PreviewCode
RTL

Count Badge

Use count and max for numeric notification badges. When count exceeds max the label renders as {max}+.

PreviewCode
RTL

Dot Indicator

Use dot for a minimal presence indicator with no label.

PreviewCode
RTL

With Icon

Use the icon slot to prepend an SVG or icon font glyph.

PreviewCode
RTL

Rounded Variants

PreviewCode
RTL

Using Badges with Other Components

Badges work great when combined with other components to show counts, status, or notifications.

Overlay / Notification Badge

Use the anchor prop with the target slot to pin a badge to the corner of any element. The host becomes position: relative and the badge is positioned absolutely at the specified corner.

PreviewCode
RTL

Four corner positions are available:

anchor valueCorner
top-end (default)Top-right
top-startTop-left
bottom-endBottom-right
bottom-startBottom-left
PreviewCode
RTL

With Icons

Combine badges with icon buttons for compact notification indicators.

PreviewCode
RTL

Multiple Badges

Stack multiple badges to show different types of information.

PreviewCode
RTL

API Reference

Attributes

AttributeTypeDefaultDescription
variant'solid' | 'flat' | 'bordered' | 'outline' | 'frost''solid'Visual style variant
color'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'-Theme color
size'sm' | 'md' | 'lg''md'Badge size
rounded'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full''full'Border radius
countnumber-Numeric count to display
maxnumber99Maximum count before showing {max}+
dotbooleanfalseShow as a dot indicator (no label)
anchor'top-end' | 'top-start' | 'bottom-end' | 'bottom-start'Pin badge to a corner of the target slot content
aria-labelstringAccessible label for assistive technology. Recommended for count-only and dot mode.

Slots

SlotDescription
(default)Badge label text
iconIcon prepended inside the badge
targetElement the badge overlays when anchor prop is set

CSS Custom Properties

PropertyDescriptionDefault
--badge-bgBackground colorTheme-dependent
--badge-colorText / icon colorTheme-dependent
--badge-border-colorBorder colorTheme-dependent
--badge-radiusBorder radiusvar(--rounded-full)
--badge-font-sizeFont sizevar(--text-xs)
--badge-font-weightFont weightvar(--font-semibold)
--badge-padding-xHorizontal paddingvar(--size-1-5)
--badge-padding-yVertical paddingvar(--size-0-5)
--badge-gapGap between icon and labelvar(--size-1)
--badge-max-widthMaximum text width24ch

Accessibility

The badge component follows WAI-ARIA best practices.

bit-badge

Screen Readers

  • Badge text content is read by screen readers as inline text.
  • Count badges expose the full value (or {max}+) as visible text that is read aloud.
  • Dot indicator badges and count-only badges convey meaning through color and shape alone — use the aria-label attribute directly on bit-badge to provide a text description for assistive technology.
  • When multiple badges are present, each should have a distinct aria-label to distinguish them.

Best Practices

Do:

  • Use count badges near buttons, nav items, or avatars to show notification totals.
  • Keep badge labels very short (1–3 words). For longer status text use bit-alert.
  • Use dot when presence alone is enough (e.g., online indicator, unread indicator).
  • Use color="error" for notification counts to draw immediate attention.

Don't:

  • Use badges as the primary call-to-action; use bit-button for that.
  • Display lengthy sentences inside a badge.