Components · Data Display
Tag
A small label chip for a category, keyword, or state — filled or bordered, with an icon, a dismiss ×, or a checkable toggle.
Examples
Colour
Shape
Closable
Checkable
Colour presets · filled
Bordered (outlined) · leading icon
Closable keywords · animates out on ✕
Checkable toggle · aria-pressed
<!-- Paste-and-run: save as .html and open in a browser. No build step.
<aha-tag> is the SAME shared custom element React and Vue consume — here in its
native form. Theming comes only from the --aha-* tokens in tokens.css.
Icons are summoned by name from the DS icon library via <aha-icon>. -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/tokens.css">
<script type="module">
import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/icons.js'; // registers <aha-icon>
import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/aha-tag.js'; // registers <aha-tag>
</script>
<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap">
<aha-tag color="primary" icon="system-tag">Poll</aha-tag>
<aha-tag color="success" icon="system-check">Published</aha-tag>
<aha-tag bordered color="processing">Scheduled</aha-tag>
<aha-tag closable id="kw">Marketing</aha-tag>
<aha-tag checkable checked id="filter">Quizzes only</aha-tag>
</div>
<script>
// closable: fires `close`, then the tag animates out + removes itself
document.getElementById('kw').addEventListener('close', () => console.log('removed'));
// checkable: fires `change` with { checked }
document.getElementById('filter').addEventListener('change', (e) => console.log('checked', e.detail.checked));
</script>
import '@ahaslides-product/design/icons'; // registers <aha-icon>
import '@ahaslides-product/design/aha-tag'; // registers <aha-tag>
import { useRef, useEffect } from 'react';
// React 18 needs a thin wrapper for the composed `close` / `change` events; React 19 can bind directly.
function AhaTag({ color, bordered, icon, closable, checkable, checked, onClose, onChange, children }) {
const ref = useRef();
useEffect(() => {
const el = ref.current;
const c = () => onClose?.();
const ch = (e) => onChange?.(e.detail.checked);
el.addEventListener('close', c);
el.addEventListener('change', ch);
return () => { el.removeEventListener('close', c); el.removeEventListener('change', ch); };
});
return (
<aha-tag ref={ref} color={color} icon={icon}
{...(bordered ? { bordered: '' } : {})}
{...(closable ? { closable: '' } : {})}
{...(checkable ? { checkable: '' } : {})}
{...(checked ? { checked: '' } : {})}
>{children}</aha-tag>
);
}
// usage
<AhaTag color="primary" icon="system-tag">Poll</AhaTag>
<AhaTag color="success" bordered icon="system-check">Published</AhaTag>
<AhaTag closable onClose={() => removeKeyword('Marketing')}>Marketing</AhaTag>
<AhaTag checkable checked onChange={(on) => setFilter(on)}>Quizzes only</AhaTag>
// main.ts — register the elements + mark aha-* as custom elements
import '@ahaslides-product/design/icons'; // registers <aha-icon>
import '@ahaslides-product/design/aha-tag'; // registers <aha-tag>
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');
// Component.vue
<script setup>
import { ref } from 'vue';
const keywords = ref(['Poll', 'Marketing']);
const quizzesOnly = ref(true);
</script>
<template>
<aha-tag color="primary" icon="system-tag">Poll</aha-tag>
<aha-tag color="success" bordered icon="system-check">Published</aha-tag>
<aha-tag
closable
@close="keywords = keywords.filter(k => k !== 'Marketing')"
>Marketing</aha-tag>
<aha-tag
checkable
:checked="quizzesOnly || undefined"
@change="quizzesOnly = $event.detail.checked"
>Quizzes only</aha-tag>
</template>
API
| Prop | Type | Default | Notes |
|---|---|---|---|
color | neutral | primary | success | processing | warning | error | neutral | Semantic tone preset (alias: `variant`) |
bordered | boolean | false | Outlined shape — white surface + a coloured 1px hairline instead of the filled tint |
icon | string | — | Leading DS icon name, e.g. system-tag (resolved via <aha-icon>) |
closable | boolean | false | Shows a dismiss × that animates out, removes the tag, and emits close |
checkable | boolean | false | Renders a selectable toggle chip (button, aria-pressed) |
checked | boolean | false | Selected state of a checkable tag (brand fill) |
close | CustomEvent | — | Composed event when the × is pressed |
change | CustomEvent | — | Composed event on a checkable toggle, detail { checked } |
Install
# .npmrc — once: point the @ahaslides-product scope at GitHub Packages
@ahaslides-product:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} # a GitHub token with read:packages
npm i @ahaslides-product/design
import '@ahaslides-product/design/tokens.css'; // once, at the app root
import '@ahaslides-product/design/aha-tag'; // registers <aha-tag>
Agent feed for this component (absolute, fetchable anywhere): tag.agent.json · tag.md · tag.llms.txt
When to use
When to use
- Tag — labelling content with a category/keyword, or showing a non-urgent state inline
- Badge — a count or a status dot attached to another element
- Button — the chip should trigger an action — a Tag is a label, not a control
Keep tag text to one or two words, sentence case. Colour carries meaning — reserve success/processing/warning/error for real states, not decoration. Use `closable` for user-removable keywords; use `checkable` for an inline filter toggle. For a domain lifecycle state prefer the status pill (aha-design-status-badges).
Surfaces
editor dashboard settings audience
Spec
Box height 22 · radius 4 · padding 0 8 · 6px gap to a 14px leading icon · Text Plus Jakarta SemiBold 600, 12/18 · Neutral bg #F1F1F1, label #4A4A4A · Primary bg #F9F5FF, label #6A1EBB · Success / Processing / Warning / Error tinted bg + matching text token · Bordered white surface + a coloured 1px hairline (the outlined DS V3 flavour) · Closable system-x ✕; animates out (scale + fade, motion-mid) then removes + emits close · Checkable outlined toggle; checked → brand fill #6A1EBB / white label; aria-pressed synced