Tag
Variants
Default
Accent
Danger
Success
Muted
Sizes
Small
Medium
Dismissible
React
Featured
JavaScript
Skill tag stack
TypeScript
React
CSS
Node.js
PostgreSQL
/*
* Tag — element (display)
*
* Inline metadata pill. Like Badge but optionally dismissible. The
* dismiss button is an opt-in <button> inside the tag; tag dismiss
* logic is the consumer's responsibility (remove the DOM node from a
* click handler).
*
* <span class="tag">Documentation</span>
* <span class="tag -accent">Featured</span>
*
* <span class="tag">
* React
* <button type="button" class="tag__dismiss" aria-label="Remove React">×</button>
* </span>
*/
@layer malevich.components {
.tag {
display: inline-flex;
align-items: center;
gap: var(--space-gap-elements-s);
padding-block: var(--space-inset-element-s);
padding-inline: var(--space-inset-element-m);
background-color: var(--color-surface-raised);
color: var(--color-ink-strong);
border: var(--border-width-hairline) solid var(--color-border-muted);
border-radius: var(--radius-pill);
font-family: var(--font-body-support-family);
font-size: var(--font-body-support-size);
font-weight: 500;
line-height: var(--font-body-support-line-height);
letter-spacing: var(--font-body-support-letter-spacing);
white-space: nowrap;
max-inline-size: 100%;
}
.tag__dismiss {
appearance: none;
-webkit-appearance: none;
background: transparent;
border: 0;
padding: 0;
margin: 0;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
inline-size: var(--size-control-s);
block-size: var(--size-control-s);
margin-inline-end: calc(-1 * var(--space-inset-element-s));
color: var(--color-ink-subtle);
border-radius: 50%;
font-family: inherit;
font-size: inherit;
line-height: 1;
transition: color var(--motion-fast) var(--motion-easing-default),
background-color var(--motion-fast) var(--motion-easing-default);
}
.tag__dismiss:hover {
color: var(--color-ink-strong);
background-color: var(--color-surface-canvas);
}
.tag__dismiss:focus-visible {
outline: var(--border-width-focus) solid var(--color-accent);
outline-offset: var(--border-width-hairline);
}
/* Variants */
.tag.-accent {
background-color: var(--color-accent-muted);
color: var(--color-accent-strong);
border-color: var(--color-accent-muted);
}
.tag.-danger {
background-color: var(--color-surface-raised);
color: var(--color-danger);
border-color: var(--color-danger);
}
.tag.-success {
background-color: var(--color-surface-raised);
color: var(--color-success);
border-color: var(--color-success);
}
.tag.-muted {
background-color: var(--color-surface-canvas);
color: var(--color-ink-regular);
}
.tag.-s {
padding-block: 0;
padding-inline: var(--space-inset-element-s);
font-size: var(--font-caption-size);
}
}
This component is pure CSS — no JavaScript required.
# Tag — AGENTS.md
> Auto-generated from `tag.docs.md` + the modifier applicability matrix.
> Edit those source files; this file regenerates on build.
## Identity
- **Component:** `tag`
- **Layer:** elements
- **Status:** stable
- **Last updated:** 2026-05-19
## Summary
- Categorize content: "React", "Design", "Tutorial".
## Variants
| Variant | Class | Use for |
|---------|------------------|----------------------------------------|
| Default | `.tag` | Neutral metadata |
| Accent | `.tag.-accent` | Highlighted (selected, featured) |
| Danger | `.tag.-danger` | Error or critical tags |
| Success | `.tag.-success` | Positive state tag |
| Muted | `.tag.-muted` | De-emphasized |
| Small | `.tag.-s` | Compact size |
## Modifier applicability
Per ADR-0007. Modifiers attach via `data-{category}="value"` attributes.
| Category | Accepts |
|---|---|
| `background` | — |
| `surface` | `flat` |
| `effect` | — |
| `shader` | — |
| `rhythm` | — |
| `motion` | any value |
| `behavior` | `dismissible` |
## Anatomy
```html
<!-- Static -->
<span class="tag">Documentation</span>
<span class="tag -accent">Featured</span>
<!-- Dismissible -->
<span class="tag">
React
<button type="button" class="tag__dismiss" aria-label="Remove React">×</button>
</span>
<!-- In a group -->
<div style="display:flex; gap:0.5rem; flex-wrap:wrap;">
<span class="tag -muted">JavaScript</span>
<span class="tag -muted">CSS</span>
<span class="tag -muted">HTML</span>
</div>
```
Click the dismiss `<button>` to handle removal:
```js
document.addEventListener("click", (e) => {
const dismiss = e.target.closest(".tag__dismiss");
if (dismiss) dismiss.closest(".tag")?.remove();
});
```
The component does not ship this handler — removal policy varies
(remove from server, mark as removed locally, undo affordance).
## Tokens consumed
- `--color-surface-raised` — default background
- `--color-surface-canvas` — muted background
- `--color-accent-muted`, `--color-accent-strong` — accent variant
- `--color-danger`, `--color-success` — status variants
- `--color-ink-strong`, `--color-ink-regular`, `--color-ink-subtle`
- `--color-border-muted` — default border
- `--radius-pill` — corner radius
- `--space-inset-element-{s,m}` — padding
- `--space-gap-elements-s` — gap before dismiss
- `--size-control-s` — dismiss button size
- `--border-width-hairline` / `--border-width-focus`
- `--font-body-{s,xs}-*` — typography
- `--motion-fast` / `--motion-easing-default`
## Accessibility contract
- Tags are decorative inline elements. Use `<span>` unless the tag is
interactive (links to a filter view → use `<a>`).
- The dismiss button is a real `<button>` with `aria-label` so
screen readers announce "Remove React".
- When removing a tag from the DOM, focus moves to the next sibling
or back to a container (consumer-defined). The component does not
manage focus on removal.
## Guidance
### Do
- Use `<span>` for static tags, `<a>` for navigable tags.
- Always pair the dismiss button with a descriptive `aria-label`.
- Keep tag labels short — 1-3 words.
### Don't
- Don't make the entire tag clickable AND include a dismiss button —
the click targets conflict.
- Don't use `<button>` as the outer element. The dismiss is the
interactive part, the tag itself is text.