Tag
This library is a work-in-progress. We are releasing it early to gather feedback, but it is not ready for production.
Tag is used to label or categorize content, providing as a visual indicator for status, types, or metadata.
Examples
Live Preview
Default
Basic tag with neutral styling.
Variants
Semantic color variants for status indication.
Shapes
Different border-radius options for visual variety.
Uppercase Modifier
Transforms text to uppercase with condensed typography.
With Close Slot
Dismissible tags using the named close slot.
CSS Shadow Parts Customization
Use CSS Shadow Parts to customize the tag's appearance without breaking encapsulation.
View Vue Code
<template>
<section>
<div class="mbe4">
<h2>Default</h2>
<p class="mbs2 mbe3">Basic tag with neutral styling.</p>
<div class="stacked-mobile">
<VueTag>
Default Tag
</VueTag>
</div>
</div>
<div class="mbe4">
<h2>Variants</h2>
<p class="mbs2 mbe3">Semantic color variants for status indication.</p>
<div class="stacked">
<VueTag
variant="info"
class="mbe2"
>Info Tag</VueTag>
<VueTag
variant="success"
class="mbe2"
>Success Tag</VueTag>
<VueTag
variant="warning"
class="mbe2"
>Warning Tag</VueTag>
<VueTag
variant="error"
class="mbe2"
>Error Tag</VueTag>
<VueTag
variant="monochrome"
class="mbe2"
>Monochrome Tag</VueTag>
</div>
</div>
<div class="mbe4">
<h2>Shapes</h2>
<p class="mbs2 mbe3">Different border-radius options for visual variety.</p>
<div class="stacked">
<VueTag
shape="pill"
variant="info"
class="mbe2"
>Pill Tag</VueTag>
<VueTag
shape="round"
variant="success"
class="mbe2"
>Round Tag</VueTag>
<VueTag
shape="circle"
variant="error"
class="mbe2"
>!</VueTag>
</div>
</div>
<div class="mbe4">
<h2>Uppercase Modifier</h2>
<p class="mbs2 mbe3">Transforms text to uppercase with condensed typography.</p>
<div class="stacked">
<VueTag
:uppercase="true"
variant="warning"
class="mbe2"
>Uppercase Tag</VueTag>
<VueTag
:uppercase="true"
shape="pill"
class="mbe2"
>PILL LABEL</VueTag>
</div>
</div>
<div class="mbe4">
<h2>With Close Slot</h2>
<p class="mbs2 mbe3">Dismissible tags using the named close slot.</p>
<div class="stacked">
<VueTag
variant="error"
class="mbe2"
>
Deletable
<button
slot="close"
aria-label="close"
>✕</button>
</VueTag>
<VueTag
variant="success"
shape="pill"
class="mbe2"
>
Active
<button
slot="close"
aria-label="close"
>✕</button>
</VueTag>
<VueTag
variant="warning"
:uppercase="true"
class="mbe2"
>
Pending
<button
slot="close"
aria-label="close"
>✕</button>
</VueTag>
</div>
</div>
<div class="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p class="mbs2 mbe3">Use CSS Shadow Parts to customize the tag's appearance without breaking encapsulation.</p>
<div class="mbe4">
<VueTag class="custom-outline-tag">Outline Tag</VueTag>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { VueTag } from "agnosticui-core/tag/vue";
</script>
<style scoped>
/* Outline tag style */
.custom-outline-tag::part(ag-tag-wrapper) {
background: transparent;
color: var(--ag-danger-text);
border: 2px solid var(--ag-danger-text);
}
</style>
Live Preview
View Lit / Web Component Code
import { LitElement, html } from 'lit';
import 'agnosticui-core/tag';
export class TagLitExamples extends LitElement {
// Render in light DOM to access global utility classes
createRenderRoot() {
return this;
}
render() {
return html`
<section>
<div class="mbe4">
<h2>Default</h2>
<p class="mbs2 mbe3">Basic tag with neutral styling.</p>
<div class="stacked-mobile">
<ag-tag>
Default Tag
</ag-tag>
</div>
</div>
<div class="mbe4">
<h2>Variants</h2>
<p class="mbs2 mbe3">Semantic color variants for status indication.</p>
<div class="stacked">
<ag-tag
variant="info"
class="mbe2"
>Info Tag</ag-tag>
<ag-tag
variant="success"
class="mbe2"
>Success Tag</ag-tag>
<ag-tag
variant="warning"
class="mbe2"
>Warning Tag</ag-tag>
<ag-tag
variant="error"
class="mbe2"
>Error Tag</ag-tag>
<ag-tag
variant="monochrome"
class="mbe2"
>Monochrome Tag</ag-tag>
</div>
</div>
<div class="mbe4">
<h2>Shapes</h2>
<p class="mbs2 mbe3">Different border-radius options for visual variety.</p>
<div class="stacked">
<ag-tag
shape="pill"
variant="info"
class="mbe2"
>Pill Tag</ag-tag>
<ag-tag
shape="round"
variant="success"
class="mbe2"
>Round Tag</ag-tag>
<ag-tag
shape="circle"
variant="error"
class="mbe2"
>!</ag-tag>
</div>
</div>
<div class="mbe4">
<h2>Uppercase Modifier</h2>
<p class="mbs2 mbe3">Transforms text to uppercase with condensed typography.</p>
<div class="stacked">
<ag-tag
uppercase
variant="warning"
class="mbe2"
>Uppercase Tag</ag-tag>
<ag-tag
uppercase
shape="pill"
class="mbe2"
>PILL LABEL</ag-tag>
</div>
</div>
<div class="mbe4">
<h2>With Close Slot</h2>
<p class="mbs2 mbe3">Dismissible tags using the named close slot.</p>
<div class="stacked">
<ag-tag
variant="error"
class="mbe2"
>
Deletable
<button
slot="close"
aria-label="close"
>✕</button>
</ag-tag>
<ag-tag
variant="success"
shape="pill"
class="mbe2"
>
Active
<button
slot="close"
aria-label="close"
>✕</button>
</ag-tag>
<ag-tag
variant="warning"
uppercase
class="mbe2"
>
Pending
<button
slot="close"
aria-label="close"
>✕</button>
</ag-tag>
</div>
</div>
<div class="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p class="mbs2 mbe3">Use CSS Shadow Parts to customize the tag's appearance without breaking encapsulation.</p>
<div class="mbe4">
<ag-tag class="custom-outline-tag">Outline Tag</ag-tag>
</div>
</div>
</section>
`;
}
}
// Register the custom element
customElements.define('tag-lit-examples', TagLitExamples);
Interactive Preview: Click the "Open in StackBlitz" button below to see this example running live in an interactive playground.
View React Code
import { ReactTag } from "agnosticui-core/tag/react";
export default function TagReactExamples() {
return (
<section>
<div className="mbe4">
<h2>Default</h2>
<p className="mbs2 mbe3">Basic tag with neutral styling.</p>
<div className="stacked-mobile">
<ReactTag>
Default Tag
</ReactTag>
</div>
</div>
<div className="mbe4">
<h2>Variants</h2>
<p className="mbs2 mbe3">Semantic color variants for status indication.</p>
<div className="stacked">
<ReactTag
variant="info"
className="mbe2"
>Info Tag</ReactTag>
<ReactTag
variant="success"
className="mbe2"
>Success Tag</ReactTag>
<ReactTag
variant="warning"
className="mbe2"
>Warning Tag</ReactTag>
<ReactTag
variant="error"
className="mbe2"
>Error Tag</ReactTag>
<ReactTag
variant="monochrome"
className="mbe2"
>Monochrome Tag</ReactTag>
</div>
</div>
<div className="mbe4">
<h2>Shapes</h2>
<p className="mbs2 mbe3">Different border-radius options for visual variety.</p>
<div className="stacked">
<ReactTag
shape="pill"
variant="info"
className="mbe2"
>Pill Tag</ReactTag>
<ReactTag
shape="round"
variant="success"
className="mbe2"
>Round Tag</ReactTag>
<ReactTag
shape="circle"
variant="error"
className="mbe2"
>!</ReactTag>
</div>
</div>
<div className="mbe4">
<h2>Uppercase Modifier</h2>
<p className="mbs2 mbe3">Transforms text to uppercase with condensed typography.</p>
<div className="stacked">
<ReactTag
uppercase
variant="warning"
className="mbe2"
>Uppercase Tag</ReactTag>
<ReactTag
uppercase
shape="pill"
className="mbe2"
>PILL LABEL</ReactTag>
</div>
</div>
<div className="mbe4">
<h2>With Close Slot</h2>
<p className="mbs2 mbe3">Dismissible tags using the named close slot.</p>
<div className="stacked">
<ReactTag
variant="error"
className="mbe2"
>
Deletable
<button
slot="close"
aria-label="close"
>✕</button>
</ReactTag>
<ReactTag
variant="success"
shape="pill"
className="mbe2"
>
Active
<button
slot="close"
aria-label="close"
>✕</button>
</ReactTag>
<ReactTag
variant="warning"
uppercase
className="mbe2"
>
Pending
<button
slot="close"
aria-label="close"
>✕</button>
</ReactTag>
</div>
</div>
<div className="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p className="mbs2 mbe3">Use CSS Shadow Parts to customize the tag's appearance without breaking encapsulation.</p>
<div className="mbe4">
<ReactTag className="custom-outline-tag">Outline Tag</ReactTag>
</div>
</div>
</section>
);
}
Usage
TIP
The framework examples below import AgnosticUI as an npm package. Alternatively, you can use the CLI for complete control, AI/LLM visibility, and full code ownership:
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add TagThe CLI copies source code directly into your project, giving you full visibility and control. After running npx ag add, you'll receive exact import instructions.
Vue
<template>
<VueTag variant="success" shape="pill" :uppercase="true">
Active Task
<button slot="close" @click="handleClose">✕</button>
</VueTag>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { VueTag } from "agnosticui-core/tag/vue";
export default defineComponent({
components: { VueTag },
methods: {
handleClose() {
// Handle close logic
},
},
});
</script>React
import { ReactTag } from "agnosticui-core/tag/react";
export default function Example() {
return (
<ReactTag variant="success" shape="pill" uppercase>
Active Task
<button slot="close" onClick={() => console.log("Close")}>
✕
</button>
</ReactTag>
);
}Lit (Web Components)
<script type="module">
import 'agnosticui-core/tag';
</script>
<ag-tag variant="success" shape="pill" uppercase>
Active Task
<button slot="close" @click=${() => console.log('Close')}>✕</button>
</ag-tag>Removable Tags
Tags can be made removable by adding the removable prop. This displays a remove button (×) and fires the tag-remove event when clicked.
Vue
<template>
<div class="tag-list">
<VueTag
v-for="tag in tags"
:key="tag.id"
:variant="tag.variant"
removable
@tag-remove="removeTag(tag.id)"
class="tag-item"
>
{{ tag.label }}
</VueTag>
</div>
</template>
<script>
import { VueTag } from "agnosticui-core/tag/vue";
export default {
components: { VueTag },
data() {
return {
tags: [
{ id: 1, label: "Design", variant: "primary" },
{ id: 2, label: "Development", variant: "success" },
{ id: 3, label: "Bug", variant: "error" },
{ id: 4, label: "Feature", variant: "info" },
],
};
},
methods: {
removeTag(id) {
this.tags = this.tags.filter((tag) => tag.id !== id);
},
},
};
</script>
<style scoped>
.tag-list {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
</style>React
import { useState } from "react";
import { ReactTag } from "agnosticui-core/tag/react";
export default function RemovableTagExample() {
const [tags, setTags] = useState([
{ id: 1, label: "Design", variant: "primary" },
{ id: 2, label: "Development", variant: "success" },
{ id: 3, label: "Bug", variant: "error" },
{ id: 4, label: "Feature", variant: "info" },
]);
const removeTag = (id) => {
setTags(tags.filter((tag) => tag.id !== id));
};
return (
<div className="tag-list">
{tags.map((tag) => (
<ReactTag
key={tag.id}
variant={tag.variant}
removable
onTagRemove={() => removeTag(tag.id)}
className="tag-item"
>
{tag.label}
</ReactTag>
))}
</div>
);
}.tag-list {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}Lit (Web Components)
import { LitElement, html, css } from "lit";
import { customElement, state } from "lit/decorators.js";
import "agnosticui-core/tag";
interface TagItem {
id: number;
label: string;
variant: string;
}
@customElement("tag-example")
export class TagExample extends LitElement {
@state() private tags: TagItem[] = [
{ id: 1, label: "Design", variant: "primary" },
{ id: 2, label: "Development", variant: "success" },
{ id: 3, label: "Bug", variant: "error" },
{ id: 4, label: "Feature", variant: "info" },
];
static styles = css`
:host {
display: block;
}
.tag-list {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.tag-item {
margin: 0.25rem;
}
`;
firstUpdated() {
// Set up event listeners for dynamically created tags in the shadow DOM
this.updateTagListeners();
}
updated() {
// Update listeners after each re-render
this.updateTagListeners();
}
private updateTagListeners() {
const tagElements = this.shadowRoot?.querySelectorAll("ag-tag");
tagElements?.forEach((tagEl, index) => {
// Remove old listener before adding new one
const clonedTagEl = tagEl.cloneNode(true);
tagEl.replaceWith(clonedTagEl);
clonedTagEl.addEventListener("tag-remove", (e: Event) => {
const customEvent = e as CustomEvent;
const tag = this.tags[index];
console.log(
"Removing tag:",
tag.label,
"variant:",
customEvent.detail.variant
);
this.tags = this.tags.filter((t) => t.id !== tag.id);
});
});
}
render() {
return html`
<div class="tag-list">
${this.tags.map(
(tag) => html`
<ag-tag class="tag-item" .variant=${tag.variant} .removable=${true}>
${tag.label}
</ag-tag>
`
)}
</div>
`;
}
}Note: When using tag components within a custom element's shadow DOM, set up event listeners in the component's lifecycle (e.g., firstUpdated() and updated()) rather than using DOMContentLoaded, as document.querySelector() cannot access elements inside shadow DOM. Use this.shadowRoot.querySelector() instead. For dynamically rendered components, you may need to update event listeners after each render.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'info' | 'warning' | 'error' | 'success' | 'primary' | '' | '' | The visual variant of the tag (e.g., for color theming). |
shape | 'pill' | 'round' | 'circle' | '' | '' | The border-radius shape of the tag. |
isUppercase | boolean | false | If true, transforms the tag text to uppercase with adjusted font sizing and letter spacing. |
removable | boolean | false | If true, shows a remove button (×) that fires the tag-remove event when clicked. |
Events
| Event | Framework | Detail | Description |
|---|---|---|---|
tag-remove | Vue: @tag-removeReact: onTagRemoveLit: @tag-remove or .onTagRemove | { variant: TagVariant } | Fired when the tag's remove button is clicked. Provides the tag variant in the detail. |
Event Patterns
AgnosticUI Tag supports three event handling patterns:
- addEventListener (Lit/Vanilla JS):
const tag = document.querySelector("ag-tag");
tag.addEventListener("tag-remove", (e) => {
console.log("Tag removed, variant:", e.detail.variant);
// Remove the tag from the DOM
tag.remove();
});- Callback props (Lit/Vanilla JS):
const tag = document.querySelector("ag-tag");
tag.onTagRemove = (e) => {
console.log("Tag removed, variant:", e.detail.variant);
tag.remove();
};- Framework event handlers (Vue/React):
<!-- Vue -->
<VueTag removable @tag-remove="handleRemove">
Removable tag
</VueTag>// React
<ReactTag removable onTagRemove={handleRemove}>
Removable tag
</ReactTag>All three patterns work identically thanks to the dual-dispatch system.
CSS Shadow Parts
| Part | Description |
|---|---|
ag-tag-wrapper | The main wrapper div for the tag's content and close slot. |
Customization Example
/* Customize the tag wrapper */
ag-tag.custom-fancy::part(ag-tag-wrapper) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-weight: 600;
padding: 0.75rem 1.25rem;
border-radius: 50px;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}Accessibility
The tag component uses a semantic <div> with ARIA attributes for the close slot (e.g., aria-label="close" on the button). Content is announced as plain text by screen readers. For uppercase mode, the visual transform does not affect screen reader output. Ensure close buttons have descriptive aria-labels for dismissible tags.
Notes
- Variants: Empty
variantuses subtle neutral styling; use 'info', 'warning', 'error', or 'success' for semantic colors. - Shapes: 'circle' is ideal for icons/single characters (e.g., "!"); it sets fixed min-width/height.
- Close Slot: Use a button with
slot="close"for dismissible tags; it auto-margins for alignment. - All implementations share the same underlying styles and behavior. Properties can be set via attributes (e.g.,
<ag-tag uppercase>) or property binding in Lit templates (e.g.,.isUppercase=${true}).