Badge
Headless inline label primitive for status, count, tag, and chip composition.
| Package | @vizejs/ui/badge |
| Maturity | stable |
| Own the source | vize lib pull badge |
| Requires | — |
| Aliases | tag, chip, status badge, count badge, unread indicator |
| Covers | HTML span, shadcn/ui Badge, Radix Themes Badge, Reka UI Primitive |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Badge } from "@vizejs/ui/badge";
Or copy the source into your project with vize lib pull badge (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Status, count, and label badges annotating items in a pull request list. -->
<script setup lang="ts">
import { Badge } from "@vizejs/ui/badge";
</script>
<template>
<ul>
<li>
Fix hydration mismatch in tabs
<Badge variant="status" tone="success">Merged</Badge>
</li>
<li>
Add calendar range selection
<Badge variant="status" tone="warning">Changes requested</Badge>
<Badge variant="count" tone="info" aria-label="12 comments">12</Badge>
</li>
<li>
Document carousel autoplay
<Badge variant="label" tone="neutral">docs</Badge>
</li>
</ul>
</template>
API
Badge
Source: src/families/feedback/badge/badge.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
as |
PrimitiveAs |
"span" |
Native element, custom element, or component to render. |
variant |
BadgeVariant |
"label" |
Usage variant mirrored to data-variant for consumer-owned styling. |
tone |
BadgeTone |
"neutral" |
Styling tone mirrored to data-tone; no CSS is emitted. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
BadgeSlotState |
Renders badge content with the current variant and tone. |
Exposed
| Member | Type | Description |
|---|---|---|
variant |
BadgeVariant |
Badge usage variant mirrored to data-variant. |
tone |
BadgeTone |
Consumer styling tone mirrored to data-tone. |
element |
BadgeElement | null |
Rendered host element or component instance. |
Behavior
Normative state x input -> outcome table for badge.vue (@vizejs/ui/badge).
Every row is proven by the named mounted-DOM or SSR test. A row without a
passing test is a contract violation.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| B1 | default | render / Tab | renders inline <span data-vize-ui="badge">, part="root", label/neutral hooks, and no focus |
renders an inline neutral label badge by default |
| B2 | status/count | render | renders the requested host while mirroring strict variant and tone tokens to data hooks | renders status and count variants without adding ARIA or focus policy |
| B3 | consumer ARIA | fallthrough attrs | preserves consumer-owned role and live-region attributes without deriving any default semantics | keeps ARIA and live-region semantics consumer owned through attrs |
| B4 | any | slot/expose | passes variant and tone to the slot and exposes the rendered element with live prop updates | passes slot state and exposes live badge state |
| B5 | SSR default | isolated requests | renders byte-identical default badge markup without request-global state | renders byte-identical label badge markup across isolated SSR requests |
| B6 | SSR custom | render | renders custom host markup with strict hooks and no default role, tabindex, or aria-hidden |
renders custom server markup without implicit accessibility attributes |
| B7 | DOM/SSR/Vapor | compile | authored SFC compiles in every renderer lane without warnings or fallback | scripts/check-renderers.ts |
| B8 | root/subpath | consumer bundle | root and subpath consumers retain only Badge, emit no CSS, and stay within gzip budget | scripts/check-tree-shaking.mjs |
Props
| Prop | Type | Purpose | Default |
|---|---|---|---|
as |
PrimitiveAs |
Native element, custom element, or component rendered as host. | "span" |
variant |
"count" | "label" | "status" |
Badge usage variant mirrored to data-variant. |
"label" |
tone |
"accent" | "danger" | "info" | "neutral" | "success" | "warning" |
Consumer styling tone mirrored to data-tone. |
"neutral" |
Slots
| Slot | Props | Purpose | Default |
|---|---|---|---|
default |
{ variant: BadgeVariant; tone: BadgeTone } |
Render semantic badge text or custom chip content. | none |
Expose
| Name | Type | Purpose | Default |
|---|---|---|---|
element |
BadgeElement | null |
Rendered host element or component instance. | null |
variant |
"count" | "label" | "status" |
Badge usage variant. | "label" |
tone |
BadgeTone |
Consumer styling tone. | "neutral" |
Data Attributes
| Attribute | Values | Purpose | Default |
|---|---|---|---|
data-vize-ui |
"badge" |
Stable family selector. | always |
data-variant |
"count", "label", "status" |
Badge usage styling hook. | "label" |
data-tone |
"accent", "danger", "info", "neutral", "success", "warning" |
Consumer tone styling hook. | "neutral" |
ARIA Attributes
Badge never sets role, tabindex, aria-hidden, aria-live, aria-label,
or aria-labelledby by default. Text content remains exposed through native
inline semantics. Consumers that need a live unread count, decorative status
dot, or labelled host pass ordinary Vue fallthrough attributes themselves.
CSS Custom Properties
The Badge component entry remains headless and emits no stylesheet. The separate
@vizejs/ui/component-badge.css export supplies optional tone, count, and
status visuals through low-specificity --vize-ui-badge-* properties. Text
content keeps its native semantics; consumers own live-region policy.
Parts
| Part | Purpose | Default |
|---|---|---|
root |
Single rendered host. | always |