oriUI

Tag

A compact label for categorizing, filtering, or annotating content — status chips, keyword badges, and removable filter pills. State is expressed through real attributes: aria-disabled disables the tag and its close button without removing it from the DOM.

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 tag is a block class plus single-class token utilities — one class repoints one token, no base class needed. The Vue props in Framework API map 1:1 to these.

ClassTypeDescription
ori-tagBlockRequired base class.
ori-variant_*Stylesolid · soft · outline · text · quiet
ori-color_*Colorprimary · secondary · success · warning · danger · info · surface
ori-font-size_*Sizexs · sm · md · lg · xl · xxl — drives the label scale
ori-size-radius_*Radiusnone · xs · sm · md · lg · xl · full
ori-tag__iconParticon element (prepend or append)
ori-tag__textPartlabel text element
ori-tag__close · ori-tag__close-iconPartclose button and its icon (present when closable)
aria-disabled=trueStatereal attribute — dims the tag and blocks pointer events

À la carte: the classes above ship in @oriui/css/components/tag.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.

Like Button, the non-fill variants (the default soft included) paint the label with the AA-safe --ori-color-text tone rather than the raw role — see Design tokens.

Variants

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

SolidSoftOutlineTextQuiet
html
<span class="ori-tag ori-variant_solid ori-color_primary ori-font-size_sm ori-size-radius_full">
    <span class="ori-tag__text">Solid</span>
</span>
<!-- swap the variant: ori-variant_solid → _soft / _outline / _text / _quiet -->
<span class="ori-tag ori-variant_soft ori-color_primary ori-font-size_sm ori-size-radius_full">
    <span class="ori-tag__text">Soft</span>
</span>
vue
<OriTag label="Solid" variant="solid" />
<OriTag label="Soft" variant="soft" />
<OriTag label="Outline" variant="outline" />
<OriTag label="Text" variant="text" />
<OriTag label="Quiet" variant="quiet" />

Colors

Every semantic role. Variant and color compose freely.

primarysecondarysuccesswarningdangerinfosurface
html
<span class="ori-tag ori-variant_soft ori-color_danger ori-font-size_sm ori-size-radius_full">
    <span class="ori-tag__text">danger</span>
</span>
vue
<OriTag label="primary" color="primary" />
<OriTag label="secondary" color="secondary" />
<OriTag label="success" color="success" />
<OriTag label="warning" color="warning" />
<OriTag label="danger" color="danger" />
<OriTag label="info" color="info" />
<OriTag label="surface" color="surface" />

Variant x color compose freely — e.g. a filled success tag or an outlined danger one:

PublishedBlockedBetaDraft
html
<span class="ori-tag ori-variant_solid ori-color_success ori-font-size_sm ori-size-radius_full">
    <span class="ori-tag__text">Published</span>
</span>
vue
<OriTag label="Published" variant="solid" color="success" />
<OriTag label="Blocked" variant="outline" color="danger" />
<OriTag label="Beta" variant="soft" color="info" />
<OriTag label="Draft" variant="text" color="warning" />

Sizes

xs → xxl. The size drives the label scale via ori-font-size_*. Default is sm.

xssmmdlgxl
html
<span class="ori-tag ori-variant_soft ori-color_primary ori-font-size_xs ori-size-radius_full">
    <span class="ori-tag__text">xs</span>
</span>
<span class="ori-tag ori-variant_soft ori-color_primary ori-font-size_lg ori-size-radius_full">
    <span class="ori-tag__text">lg</span>
</span>
vue
<OriTag label="xs" size="xs" />
<OriTag label="sm" size="sm" />
<OriTag label="md" size="md" />
<OriTag label="lg" size="lg" />
<OriTag label="xl" size="xl" />

Radius

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

nonesmmdlgfull
html
<span class="ori-tag ori-variant_soft ori-color_primary ori-font-size_sm ori-size-radius_none">
    <span class="ori-tag__text">none</span>
</span>
vue
<OriTag label="none" radius="none" />
<OriTag label="sm" radius="sm" />
<OriTag label="md" radius="md" />
<OriTag label="full" radius="full" />

With icons

prependIcon places an icon before the label; appendIcon places one after. Both accept an SVG path string. They can be used together.

VerifiedExternalWarning
html
<span class="ori-tag ori-variant_soft ori-color_primary ori-font-size_sm ori-size-radius_full">
    <i class="ori-icon ori-tag__icon" aria-hidden="true">
        <svg viewBox="0 0 24 24"><path d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z" /></svg>
    </i>
    <span class="ori-tag__text">Verified</span>
</span>
vue
<OriTag label="Verified" prependIcon="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z" />
<OriTag label="External" appendIcon="M19 19H5V5h7V3H5…" color="info" />
<OriTag label="Warning" prependIcon="M1 21h22L12 2…" color="warning" variant="solid" />

Closable

closable appends a close button. Clicking it emits a close event — your handler removes the tag. closeLabel sets the button's aria-label (default Remove).

ReactVueSvelte
html
<span class="ori-tag ori-variant_soft ori-color_primary ori-font-size_sm ori-size-radius_full">
    <span class="ori-tag__text">React</span>
    <button type="button" class="ori-tag__close" aria-label="Remove">
        <i class="ori-icon ori-tag__close-icon" aria-hidden="true">
            <svg viewBox="0 0 24 24">
                <path d="M6.4 19 5 17.6l5.6-5.6L5 6.4 6.4 5l5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6Z" />
            </svg>
        </i>
    </button>
</span>
vue
<OriTag v-for="tag in tags" :key="tag" :label="tag" closable @close="removeTag(tag)" />

Disabled

disabled applies aria-disabled="true" to the root span, dimming the tag and blocking pointer events including the close button.

ArchivedArchivedArchived
html
<span class="ori-tag ori-variant_soft ori-color_primary ori-font-size_sm ori-size-radius_full" aria-disabled="true">
    <span class="ori-tag__text">Archived</span>
</span>
vue
<OriTag label="Archived" disabled />
<OriTag label="Archived" disabled variant="outline" />
<OriTag label="Archived" disabled closable />

Common patterns

A filter chip list and a status badge in a table row — the everyday compositions.

VueTypeScriptVite
html
<!-- filter chip list -->
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem">
    <span class="ori-tag ori-variant_soft ori-color_success ori-font-size_sm ori-size-radius_full">
        <span class="ori-tag__text">Vue</span>
        <button type="button" class="ori-tag__close" aria-label="Remove">
            <i class="ori-icon ori-tag__close-icon" aria-hidden="true">
                <svg viewBox="0 0 24 24">
                    <path
                        d="M6.4 19 5 17.6l5.6-5.6L5 6.4 6.4 5l5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6Z"
                    />
                </svg>
            </i>
        </button>
    </span>
</div>
vue
<!-- filter chip list -->
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem">
    <OriTag
        v-for="filter in activeFilters"
        :key="filter.value"
        :label="filter.label"
        :color="filter.color"
        closable
        @close="removeFilter(filter.value)"
    />
</div>

Accessibility

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

  • The root element is a <span> (non-interactive). When the tag is purely decorative or informational, no additional role is required.
  • disabled sets aria-disabled="true" (not the boolean disabled) on the root span — the tag stays in the accessibility tree. The nested close <button> gets the boolean disabled attribute so it is unreachable via keyboard when the tag is disabled.
  • The close button has an explicit aria-label (default Remove); it is a real <button type="button"> with a visible :focus-visible ring.
  • Icon elements inside the tag carry aria-hidden="true" — they are decorative.
  • When tags are part of an interactive group (e.g. a filter chip list), wrap the group in an element with role="group" and an aria-label describing the set (e.g. aria-label="Active filters").
KeyElementAction
TabClose buttonMoves focus to the close button.
EnterClose buttonActivates the close button (emits close).
SpaceClose buttonActivates the close button (native).

Framework API

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

Props

PropTypeDefaultDescription
appendIconstring—SVG path for an icon rendered after the label.
closablebooleanfalseShows a close button that emits close when clicked.
closeLabelstring'Remove'aria-label for the close button.
colorThemeColor'primary'Semantic role: primary · secondary · success · warning · danger · info · surface.
disabledbooleanfalseSets aria-disabled="true" on the root and disabled on the close button.
prependIconstring—SVG path for an icon rendered before the label.
radiusRadiusSize'full'Corner radius (none · xs · sm · md · lg · xl · full).
sizeActionSize'sm'Label scale (xs · sm · md · lg · xl · xxl).
labelstring—Tag text. Alternatively use the default slot.
variant'solid' | 'soft' | 'outline' | 'text' | 'quiet''soft'Visual style.

Events & attributes

EventPayloadDescription
close—Emitted when the close button is clicked (closable only).

OriTag does not set inheritAttrs: false, so any extra attributes (class, data-*, aria-label, …) fall through to the root <span>.

Slots

SlotDescription
prependDecorator before the label. Falls back to the prependIcon <ori-icon> when not provided.
defaultReplaces the label prop content. Rendered inside ori-tag__text; use for rich labels.
appendDecorator after the label. Falls back to the appendIcon <ori-icon> when not provided.