oriUI

Tag

A compact label for categorising, 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 organised 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_*Stylefill · tonal · outline · text · plain
ori-color_*Colorprimary · secondary · success · warn · danger · info · surface
ori-font-size_*Sizexs · sm · md · lg · xl · xxl — drives the label scale
ori-size-radius_*Radiuszero · xs · sm · md · lg · xl · rounded
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

Like Button, the non-fill variants (the default tonal 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.

FillTonalOutlineTextPlain
html
<span class="ori-tag ori-variant_fill ori-color_primary ori-font-size_sm ori-size-radius_rounded">
    <span class="ori-tag__text">Fill</span>
</span>
<!-- swap the variant: ori-variant_fill → _tonal / _outline / _text / _plain -->
<span class="ori-tag ori-variant_tonal ori-color_primary ori-font-size_sm ori-size-radius_rounded">
    <span class="ori-tag__text">Tonal</span>
</span>
vue
<OriTag text="Fill" variant="fill" />
<OriTag text="Tonal" variant="tonal" />
<OriTag text="Outline" variant="outline" />
<OriTag text="Text" variant="text" />
<OriTag text="Plain" variant="plain" />

Colors

Every semantic role. Variant and color compose freely.

primarysecondarysuccesswarndangerinfosurface
html
<span class="ori-tag ori-variant_tonal ori-color_danger ori-font-size_sm ori-size-radius_rounded">
    <span class="ori-tag__text">danger</span>
</span>
vue
<OriTag text="primary" color="primary" />
<OriTag text="secondary" color="secondary" />
<OriTag text="success" color="success" />
<OriTag text="warn" color="warn" />
<OriTag text="danger" color="danger" />
<OriTag text="info" color="info" />
<OriTag text="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_fill ori-color_success ori-font-size_sm ori-size-radius_rounded">
    <span class="ori-tag__text">Published</span>
</span>
vue
<OriTag text="Published" variant="fill" color="success" />
<OriTag text="Blocked" variant="outline" color="danger" />
<OriTag text="Beta" variant="tonal" color="info" />
<OriTag text="Draft" variant="text" color="warn" />

Sizes

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

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

Radius

From zero (square) to the default rounded (pill).

zerosmmdlgrounded
html
<span class="ori-tag ori-variant_tonal ori-color_primary ori-font-size_sm ori-size-radius_zero">
    <span class="ori-tag__text">zero</span>
</span>
vue
<OriTag text="zero" radius="zero" />
<OriTag text="sm" radius="sm" />
<OriTag text="md" radius="md" />
<OriTag text="rounded" radius="rounded" />

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_tonal ori-color_primary ori-font-size_sm ori-size-radius_rounded">
    <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 text="Verified" prependIcon="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z" />
<OriTag text="External" appendIcon="M19 19H5V5h7V3H5…" color="info" />
<OriTag text="Warning" prependIcon="M1 21h22L12 2…" color="warn" variant="fill" />

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_tonal ori-color_primary ori-font-size_sm ori-size-radius_rounded">
    <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" :text="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_tonal ori-color_primary ori-font-size_sm ori-size-radius_rounded" aria-disabled="true">
    <span class="ori-tag__text">Archived</span>
</span>
vue
<OriTag text="Archived" disabled />
<OriTag text="Archived" disabled variant="outline" />
<OriTag text="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_tonal ori-color_success ori-font-size_sm ori-size-radius_rounded">
        <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"
        :text="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. (Svelte bindings are planned.)

Props

PropTypeDefaultDescription
appendIconstringSVG 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 · warn · danger · info · surface.
disabledbooleanfalseSets aria-disabled="true" on the root and disabled on the close button.
prependIconstringSVG path for an icon rendered before the label.
radiusRadiusSize'rounded'Corner radius (zero · xs · sm · md · lg · xl · rounded).
sizeActionSize'sm'Label scale (xs · sm · md · lg · xl · xxl).
textstringTag label. Alternatively use the default slot.
variant'fill' | 'tonal' | 'outline' | 'text' | 'plain''tonal'Visual style.

Events & attributes

EventPayloadDescription
closeEmitted 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 text 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.