oriUI

Badge

A small status or count indicator. Attach it to any element as a floating pip, drop it inline next to text, or render it standalone — all three modes share the same class surface.

The examples are organized by layer: the class reference is the standalone @oriui/css layer, and the Framework API is the @oriui/vue component. Every example is live — flip its code between HTML (the standalone classes, also your htmx / Astro / Svelte / plain-HTML usage), Vue; HTML is the default.

Classes

A badge is a block class plus single-class token utilities — one class repoints one token; no base class is needed. The Vue props in Framework API map 1:1 to these. Two layout modifiers (ori-badge_dot, ori-badge_floating) and the wrapper class (ori-badge-anchor) complete the floating pattern.

ClassTypeDescription
ori-badgeBlockRequired base class. Defaults baked in: solid variant, primary color, full radius.
ori-variant_*Stylesolid · soft · outline · text · quiet
ori-color_*Colorprimary · secondary · success · warning · danger · info · surface · background
ori-size-radius_*Radiusnone · xs · sm · md · lg · xl · full
ori-badge_dotLayoutcollapses the badge to a small filled circle; content is hidden
ori-badge_floatingLayoutpositions the badge in the top-end corner of its anchor wrapper
ori-badge-anchorWrapperposition:relative wrapper required for floating badges

À la carte: the classes above ship in @oriui/css/components/badge.css. .ori-badge-anchor lives in badge.css. Import a foundation (@oriui/css/base.css or @oriui/css/tokens.css) first — the token utilities (ori-color_*, ori-size-radius_*, …) live there, not in the component file. The full bundle @oriui/css is the default and already carries both; see à-la-carte imports.

Variants

Five visual styles, driven by the ori-variant_* single-class token.

SolidSoftOutlineTextQuiet
html
<span class="ori-badge">Solid</span>
<!-- swap the variant: ori-variant_soft / _outline / _text / _quiet -->
<span class="ori-badge ori-variant_soft">Soft</span>
vue
<OriBadge content="Solid" variant="solid" />
<OriBadge content="Soft" variant="soft" />
<OriBadge content="Outline" variant="outline" />
<OriBadge content="Text" variant="text" />
<OriBadge content="Quiet" variant="quiet" />

Colors

All semantic roles. The badge inherits the shared ori-color_* token, so variant × color compose freely.

primarysecondarysuccesswarningdangerinfosurface
html
<span class="ori-badge ori-color_danger">danger</span>
vue
<OriBadge content="primary" color="primary" />
<OriBadge content="danger" color="danger" />
<OriBadge content="info" color="info" />

Variant × color compose freely — e.g. a soft success badge or an outline danger badge:

ApprovedErrorBeta
html
<span class="ori-badge ori-variant_soft ori-color_success">Approved</span>
<span class="ori-badge ori-variant_outline ori-color_danger">Error</span>
vue
<OriBadge content="Approved" variant="soft" color="success" />
<OriBadge content="Error" variant="outline" color="danger" />
<OriBadge content="Beta" variant="soft" color="info" />

Radius

From none (square) to the default full (pill).

nonesmmdlgfull
html
<span class="ori-badge ori-size-radius_none">none</span> <span class="ori-badge">full</span>
vue
<OriBadge content="none" radius="none" />
<OriBadge content="full" radius="full" />

Count & max

Pass a number to content. Set max to cap the display value — any number exceeding max renders as {max}+.

54299+9
html
<span class="ori-badge">5</span>
<!-- cap manually in your template when not using Vue -->
<span class="ori-badge ori-color_danger">99+</span>
vue
<OriBadge :content="5" />
<OriBadge :content="42" />
<!-- 130 > 99 → renders "99+" -->
<OriBadge :content="130" :max="99" color="danger" />

Dot

dot collapses the badge to a small filled circle. Content is hidden; it is always decorative (aria-hidden="true") unless you give it an aria-label.

html
<!-- dot is always aria-hidden unless it has an aria-label -->
<span class="ori-badge ori-badge_dot ori-color_danger" aria-hidden="true"></span>
vue
<OriBadge dot />
<OriBadge dot color="danger" />
<OriBadge dot color="success" />

Floating

The badge floats over wrapped content — floating needs the badge to wrap an anchor. In Vue, pass the anchor into the default slot and add the floating prop (without a default slot floating has nothing to position against and is ignored). In HTML, wrap the anchor in ori-badge-anchor and add ori-badge_floating to the badge itself.

399+
html
<!-- wrap anchor content in ori-badge-anchor, add ori-badge_floating to the badge -->
<span class="ori-badge-anchor">
    <button class="ori-button ori-variant_soft ori-color_primary ori-size-radius_full">Inbox</button>
    <span class="ori-badge ori-badge_floating ori-color_danger" aria-label="3 unread">3</span>
</span>
vue
<!-- floating over a button -->
<OriBadge :content="3" floating color="danger">
    <button class="ori-button ori-variant_soft ori-color_primary ori-size-radius_full">
        Inbox
    </button>
</OriBadge>

<!-- dot variant -->
<OriBadge dot floating color="danger">
    <button class="ori-button …">Notifications</button>
</OriBadge>

Common patterns

A notification bell, an avatar with a status pip, and a tab bar with unread counts.

399+
New
html
<!-- notification bell -->
<span class="ori-badge-anchor">
    <button class="ori-button …" aria-label="Notifications"><!-- bell icon --></button>
    <span class="ori-badge ori-badge_floating ori-color_danger" aria-label="12 unread notifications">12</span>
</span>

<!-- status dot on avatar -->
<span class="ori-badge-anchor">
    <div class="ori-avatar …"><!-- avatar --></div>
    <span class="ori-badge ori-badge_dot ori-badge_floating ori-color_success" aria-label="Online"></span>
</span>

<!-- inline tab counter -->
<span style="display: inline-flex; align-items: center; gap: 0.5rem">
    Messages
    <span class="ori-badge ori-variant_soft">5</span>
</span>
vue
<!-- notification bell -->
<OriBadge
    :content="notifications"
    :max="99"
    floating
    color="danger"
    :aria-label="`${notifications} unread notifications`"
>
    <OriButton icon="M12 22c1.1 0 2-.9 2-2h-4a2 2 0 0 0 2 2zm6-6V11c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5S10.5 3.17 10.5 4v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z" aria-label="Notifications" />
</OriBadge>

<!-- status dot on an avatar -->
<OriBadge dot floating color="success" aria-label="Online">
    <OriAvatar name="Ada Lovelace" />
</OriBadge>

<!-- tab with unread count -->
<span style="display: inline-flex; align-items: center; gap: 0.5rem">
    Messages
    <OriBadge :content="5" variant="soft" color="primary" />
</span>

Accessibility

The accessibility contract holds across every layer — the standalone classes and the Vue component render the same attributes.

  • A badge with visible text content (content) conveys that text to assistive technology directly.
  • A pure dot badge — or an empty badge with no aria-label — is marked aria-hidden="true" because it carries no information for screen readers.
  • Supply an aria-label whenever the badge conveys meaning that a sighted user can read but a screen reader cannot (e.g. a dot meaning "online", or a floating count with no textual fallback nearby).
  • A floating count that visually annotates an already-labeled control (e.g. "Inbox (3 unread)") should carry aria-label on the badge element or be hidden with aria-hidden="true" if the parent label already includes the count.
  • The badge is a <span> — it has no interactive role and no keyboard contract of its own. When a badge and its anchor together form an interactive control, apply focus management and ARIA to the outer interactive element.
AttributeConditionValue
aria-hidden="true"dot with no aria-label, or an empty badge with no aria-label"true"
aria-labelariaLabel is setits value

Framework API

The props, events, and slots of the Vue component. The standalone CSS layer has no component API — its surface is the classes above.

Props

PropTypeDefaultDescription
ariaLabelstring—Accessible name (aria-label in a template). Needed by a dot or a count that means something a sighted user reads off the screen.
colorThemeColor'primary'Semantic color role: primary · secondary · success · warning · danger · info · surface · background.
contentstring | number—Text or number to display. Numbers are capped to max+ when max is set.
dotbooleanfalseCollapses the badge to a small filled circle; content is hidden.
floatingbooleanfalsePositions the badge in the top-end corner of the default-slot anchor (requires a default slot child).
maxnumber—When content is a number and exceeds max, the display value is capped to {max}+.
radiusRadiusSize'full'Corner radius: none · xs · sm · md · lg · xl · full.
variantVariant'solid'Visual style: solid · soft · outline · text · quiet.

Events & attributes

OriBadge declares no custom events. It sets inheritAttrs: false and applies v-bind="$attrs" directly to the inner .ori-badge element — so id, class, data-*, and event listeners always land on the badge element regardless of whether a default slot (the anchor wrapper) is present.

Slots

SlotDescription
defaultThe element the badge floats over. When provided, the component wraps both the slot content and the badge in an ori-badge-anchor span. When absent, the badge renders standalone.
contentCustom badge content, replacing the capped content value ({max}+) that renders by default. Only renders when not a dot. A badge that uses this slot is kept in the a11y tree (not decorative).