Skip to content

Tag

Experimental Alpha

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

Vue
Lit
React
Live Preview

Default

Basic tag with neutral styling.

Default Tag

Variants

Semantic color variants for status indication.

Info TagSuccess TagWarning TagError TagMonochrome Tag

Shapes

Different border-radius options for visual variety.

Pill TagRound Tag!

Uppercase Modifier

Transforms text to uppercase with condensed typography.

Uppercase TagPILL LABEL

With Close Slot

Dismissible tags using the named close slot.

Deletable Active Pending

CSS Shadow Parts Customization

Use CSS Shadow Parts to customize the tag's appearance without breaking encapsulation.

Outline Tag
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>
  );
}
Open in StackBlitz

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:

bash
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add Tag

The 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
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
tsx
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)
html
<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
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
tsx
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>
  );
}
css
.tag-list {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}
Lit (Web Components)
typescript
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

PropTypeDefaultDescription
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.
isUppercasebooleanfalseIf true, transforms the tag text to uppercase with adjusted font sizing and letter spacing.
removablebooleanfalseIf true, shows a remove button (×) that fires the tag-remove event when clicked.

Events

EventFrameworkDetailDescription
tag-removeVue: @tag-remove
React: onTagRemove
Lit: @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:

  1. addEventListener (Lit/Vanilla JS):
javascript
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();
});
  1. Callback props (Lit/Vanilla JS):
javascript
const tag = document.querySelector("ag-tag");
tag.onTagRemove = (e) => {
  console.log("Tag removed, variant:", e.detail.variant);
  tag.remove();
};
  1. Framework event handlers (Vue/React):
vue
<!-- Vue -->
<VueTag removable @tag-remove="handleRemove">
  Removable tag
</VueTag>
tsx
// React
<ReactTag removable onTagRemove={handleRemove}>
  Removable tag
</ReactTag>

All three patterns work identically thanks to the dual-dispatch system.

CSS Shadow Parts

PartDescription
ag-tag-wrapperThe main wrapper div for the tag's content and close slot.

Customization Example

css
/* 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 variant uses 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}).